# GreptimeDB Documentation > GreptimeDB is an open-source observability database for metrics, logs, traces, and wide events. Drop-in replacement for Prometheus, Loki & Elasticsearch, or the single backend for OpenTelemetry. This file contains all documentation content in a single document following the llmstxt.org standard. ## GreptimeDB # GreptimeDB Documentation **GreptimeDB** is an open-source observability database that stores metrics, logs, and traces in one engine. Run it as the single OpenTelemetry backend in place of Prometheus, Loki, and Elasticsearch, keep the data on object storage, and query all of it with [SQL](/user-guide/query-data/sql.md) and [PromQL](/user-guide/query-data/promql.md). - **[Getting Started](/getting-started/overview.md)** Install GreptimeDB, write your first rows, and run your first query. - **[User Guide](/user-guide/overview.md)** Ingestion protocols, queries, pipelines, flows, and running GreptimeDB in production. - **[For AI Agents](/faq-and-others/vibecoding.md)** Run GreptimeDB from a coding agent, with no custom integration. - **[Reference](/reference/sql/overview.md)** SQL syntax, functions, and data types. ## Ask about GreptimeDB Installation, ingestion, queries, configuration — answered from this documentation. ## Why GreptimeDB **Replace three systems with one.** Metrics, logs, and traces go into one columnar engine with native OpenTelemetry support, instead of a separate store, query language, and operational overhead for each signal. **Pay object-storage prices.** S3, Azure Blob, and GCS are the primary store, and compute scales independently of them. Columnar storage and observability-tuned compression cut cost by up to 50x — [OceanBase Cloud](/user-guide/concepts/why-greptimedb.md#what-production-users-report) keeps 300 TB of logs and audit data on GreptimeDB and reports 60%+ lower storage cost after moving from Loki. **Keep your collectors and dashboards.** [Prometheus remote write](/user-guide/ingest-data/for-observability/prometheus.md) and [OTLP](/user-guide/ingest-data/for-observability/opentelemetry.md) on the write path, [PromQL](/user-guide/query-data/promql.md) and the [Jaeger API](/user-guide/query-data/jaeger.md) on the read path, each on its own HTTP endpoint. SQL arrives over the [MySQL](/user-guide/protocols/mysql.md) and [PostgreSQL](/user-guide/protocols/postgresql.md) wire protocols. [Grafana](/user-guide/integrations/grafana.md) dashboards keep working. Each [protocol page](/user-guide/protocols/overview.md) states what it covers and what it does not. **Give agents one query interface.** In [Agent RCA Bench](https://rca-bench.greptime.com/), six models investigated the same 14 incidents on GreptimeDB and on Prometheus, Loki, and Tempo: 40% fewer wrong diagnoses, 48% fewer input tokens read, about 45% lower cost to run. More in [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md) and [Observability 2.0 and wide events](/user-guide/concepts/observability-2.md). ## Build with AI agents The quickstart guide is hosted as a fetchable Markdown file. A coding agent reads it at runtime, with no installation required. > **AGENT ONBOARDING:** Read https://docs.greptime.com/SKILL.md and follow the instructions to use GreptimeDB with your AI agent — deploy, configure, ingest, and query. - **[MCP Server](/user-guide/integrations/mcp.md)** — query tools an agent can call: SQL, TQL, and range queries, read-only by default. It also manages pipelines and dashboards, which write. - **[Skills](/faq-and-others/vibecoding.md)** — pipelines, flows, triggers, table design, and performance diagnosis, in the [Agent Skills](https://agentskills.io/) format. - **[Semantic layer](/user-guide/concepts/semantic-layer.md)** — `greptime.semantic.*` table metadata recording the signal, source, and unit behind each table. - **[llms.txt](https://docs.greptime.com/llms.txt)** — a structured index of the whole site; append `.md` to any page URL for its raw Markdown. ## Keep exploring - [Tutorials](/tutorials/k8s-metrics-monitor.md): end-to-end walkthroughs, such as monitoring a Kubernetes cluster. - [GreptimeDB Enterprise](/enterprise/overview.md): read replicas, workload isolation, RBAC, audit logging, and disaster recovery. - [Contributor Guide](/contributor-guide/overview.md): internals, for anyone working on GreptimeDB itself. - [Roadmap](https://greptime.com/blogs/2026-02-11-greptimedb-roadmap-2026): the project's direction and planned work. - [Release Notes](/release-notes): every released version. - [FAQ](/faq-and-others/faq.md): common questions about deployment, ingestion, and queries. --- ## GreptimeDB Cluster The GreptimeDB cluster can run in cluster mode to scale horizontally. ## Deploy the GreptimeDB cluster in Kubernetes For production environments, we recommend deploying the GreptimeDB cluster in Kubernetes. Please refer to [Deploy on Kubernetes](/user-guide/deployments-administration/deploy-on-kubernetes/overview.md). ## Use Docker Compose :::tip NOTE Although Docker Compose is a convenient way to run the GreptimeDB cluster, it is only for development and testing purposes. For production environments or benchmarking, we recommend using Kubernetes. ::: ### Prerequisites Using Docker Compose is the easiest way to run the GreptimeDB cluster. Before starting, make sure you have already installed the Docker. ### Step1: Download the YAML file for Docker Compose ``` wget https://raw.githubusercontent.com/GreptimeTeam/greptimedb/v1.2.1/docker/docker-compose/cluster-with-etcd.yaml ``` ### Step2: Start the cluster ``` GREPTIMEDB_VERSION=v1.2.1 \ docker compose -f ./cluster-with-etcd.yaml up ``` If the cluster starts successfully, it will listen on 4000-4003. , you can access the cluster by referencing the [Quick Start](../quick-start.md). ### Clean up You can use the following command to stop the cluster: ``` docker compose -f ./cluster-with-etcd.yaml down ``` By default, the data will be stored in `./greptimedb-cluster-docker-compose`. You also can remove the data directory if you want to clean up the data. ## Next Steps Learn how to write data to GreptimeDB in [Quick Start](../quick-start.md). --- ## GreptimeDB Dashboard GreptimeDB stores metrics, logs, and traces in a single database. The [Dashboard](https://github.com/GreptimeTeam/dashboard) is a built-in web UI for exploring that data after installation — no extra components required. For the motivation behind unified observability storage, see [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md). ## Access the Dashboard The Dashboard is embedded in the GreptimeDB binary since v0.2.0. After starting [GreptimeDB Standalone](greptimedb-standalone.md) or [GreptimeDB Cluster](greptimedb-cluster.md), open: ``` http://localhost:4000/dashboard ``` To disable the Dashboard HTTP service, set `--disable-dashboard` when starting the frontend. See [frontend command-line options](/reference/command-lines/frontend.md). ## Explore your data The Dashboard provides a general query entry and signal-specific views: | View | Data scope | Description | | --- | --- | --- | | [Table Query](#table-query) | Any data | General query entry. Switch between SQL and PromQL in the editor. | | [Metrics Query](#metrics-query) | Metrics | Browse and query metrics with a PromQL-oriented UI. | | [Logs Query](#logs-query) | Logs | Filter and search logs with a builder or code editor. | | [Traces Query](#traces-query) | Traces | Search traces and inspect spans with a builder or code editor. | ## Table Query Table Query is the Dashboard's general query entry. You can query any data in GreptimeDB and switch between SQL and PromQL in the editor. Browse databases and tables on the left, run queries with **Run Query**, **Explain Query**, or **Run All**, and view results below. ![Table Query](/dashboard/tablequery.png) - Browse databases and tables in the left panel. - Write multiple queries in a single editor. - Inspect results in a table with pagination. - Use **Explain Query** to visualize [`EXPLAIN ANALYZE`](/reference/sql/explain.md) execution plans. See [SQL](/user-guide/query-data/sql.md) and [PromQL](/user-guide/query-data/promql.md) for query syntax. ## Metrics Query Metrics Query is a metrics-specific view. Browse available metrics, enter a metric name or PromQL expression, and switch between list and chart views. ![Metrics Query](/dashboard/metricquery.png) - Search and browse metrics in the left panel. - Run queries from the editor and view label sets with values. - Toggle between list and chart display. See [PromQL](/user-guide/query-data/promql.md) for query syntax. You can also run PromQL from [Table Query](#table-query). ## Logs Query Logs Query is a logs-specific view. Select a database and log table, add filters, and inspect log lines. Use **Builder** for point-and-click queries or **Code** to write queries directly. ![Logs Query](/dashboard/logsquery.png) - Set time range, filters, order, and limit in the builder. - View a row-count chart over time above the results. - Enable **Live** for streaming results or export results as CSV. See [Log Query](/user-guide/query-data/log-query.md) and [Logs](/user-guide/logs/overview.md) for more details. ## Traces Query Traces Query is a traces-specific view. Select a trace table, filter by trace ID or span attributes, and inspect trace records. ![Traces Query](/dashboard/tracesquery.png) - Use **Builder** or **Code** to compose queries. - Filter root spans (for example, `parent_span_id` not exist) or search by `trace_id`. See [Traces](/user-guide/traces/overview.md) and [Jaeger](/user-guide/query-data/jaeger.md) for more details. ## Visualization The Dashboard embeds [Perses](https://perses.dev/) for building observability dashboards. Click **Visualization** in the sidebar to create, edit, and browse dashboards — no separate Perses installation required. GreptimeDB pre-configures two datasource plugins that connect to your local instance: | Query type | Datasource plugin | Panel types | Use for | | --- | --- | --- | --- | | **PromQL** | `PrometheusDatasource` | `TimeSeriesChart`, `GaugeChart`, `StatChart` | Prometheus metrics, `node_exporter`, migrated Grafana dashboards | | **SQL time series** | `GreptimeDBDatasource` | `TimeSeriesChart`, `StatChart`, `Table` | GreptimeDB tables with `RANGE`, `ALIGN`, and `FILL` | | **Logs** | `GreptimeDBDatasource` | `LogsTable` | Log tables with SQL filters and dashboard variables | | **Traces** | `GreptimeDBDatasource` | `TraceTable`, `TracingGanttChart` | Trace tables — trace list and per-trace Gantt view | For example, the built-in **Node Exporter** dashboard uses PromQL to show CPU, memory, disk, and network panels with variables such as `job` and `host`: ![Visualization](/dashboard/visualization.png) See [Perses integration](/user-guide/integrations/perses.md) for datasource connection, panel configuration, and Grafana migration. ## More capabilities The Dashboard also includes: ### Logs Pipelines Create, edit, and test log pipelines in the UI. See [Manage Pipelines](/user-guide/logs/manage-pipelines.md). ### Ingest Write data manually in [InfluxDB Line Protocol](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md) format. ### Flow View and manage Flow tasks for continuous computation. See [Manage Flow](/user-guide/flow-computation/manage-flow.md). --- ## GreptimeDB Standalone We use the simplest configuration for you to get started. For a comprehensive list of configurations available in GreptimeDB, see the [configuration documentation](/user-guide/deployments-administration/configuration.md). ## Deploy the GreptimeDB standalone in Kubernetes For production environments, we recommend deploying the GreptimeDB standalone in Kubernetes. Please refer to [Deploy on Kubernetes](/user-guide/deployments-administration/deploy-on-kubernetes/overview.md). ## Binary ### Download from website You can try out GreptimeDB by downloading the latest stable build releases from the [Download page](https://greptime.com/download). ### Linux and macOS For Linux and macOS users, you can download the latest build of the `greptime` binary by using the following commands: ```shell curl -fsSL \ https://raw.githubusercontent.com/greptimeteam/greptimedb/main/scripts/install.sh | sh -s v1.2.1 ``` Once the download is completed, the binary file `greptime` will be stored in your current directory. You can run GreptimeDB in the standalone mode: ```shell ./greptime standalone start ``` ### Windows If you have WSL([Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/about)) enabled, you can launch a latest Ubuntu and run GreptimeDB like above! Otherwise please download the GreptimeDB binary for Windows at our [official site](https://greptime.com/resources), and unzip the downloaded artifact. To run GreptimeDB in standalone mode, open a terminal (like Powershell) at the directory where the GreptimeDB binary locates, and execute: ```shell .\greptime standalone start ``` ## Docker Make sure the [Docker](https://www.docker.com/) is already installed. If not, you can follow the official [documents](https://www.docker.com/get-started/) to install Docker. ```shell docker run -p 127.0.0.1:4000-4003:4000-4003 \ -v "$(pwd)/greptimedb_data:/greptimedb_data" \ --name greptime --rm \ greptime/greptimedb:v1.2.1 standalone start \ --http-addr 0.0.0.0:4000 \ --grpc-bind-addr 0.0.0.0:4001 \ --mysql-addr 0.0.0.0:4002 \ --postgres-addr 0.0.0.0:4003 ``` :::tip NOTE To avoid accidentally exit the Docker container, you may want to run it in the "detached" mode: add the `-d` flag to the `docker run` command. ::: The data will be stored in the `greptimedb_data/` directory in your current directory. If you want to use another version of GreptimeDB's image, you can download it from our [GreptimeDB Dockerhub](https://hub.docker.com/r/greptime/greptimedb). In particular, we support GreptimeDB based on CentOS, and you can try image `greptime/greptimedb-centos`. :::tip NOTE If you are using a Docker version lower than [v23.0](https://docs.docker.com/engine/release-notes/23.0/), you may experience problems with insufficient permissions when trying to run the command above, due to a [bug](https://github.com/moby/moby/pull/42681) in the older version of Docker Engine. You can: 1. Set `--security-opt seccomp=unconfined`, for example: ```shell docker run --security-opt seccomp=unconfined -p 4000-4003:4000-4003 \ -v "$(pwd)/greptimedb_data:/greptimedb_data" \ --name greptime --rm \ greptime/greptimedb:v1.2.1 standalone start \ --http-addr 0.0.0.0:4000 \ --grpc-bind-addr 0.0.0.0:4001 \ --mysql-addr 0.0.0.0:4002 \ --postgres-addr 0.0.0.0:4003 ``` 2. Upgrade the Docker version to v23.0.0 or higher; ::: ## Binding address GreptimeDB binds to `127.0.0.1` by default. If you need to accept connections from other addresses, you can start with the following parameters. > :::danger Warning > If the computer running GreptimeDB is directly exposed to the internet, binding to `0.0.0.0` is dangerous and will expose the instance to everybody on the internet. ```shell ./greptime standalone start \ --http-addr 0.0.0.0:4000 \ --grpc-bind-addr 0.0.0.0:4001 \ --mysql-addr 0.0.0.0:4002 \ --postgres-addr 0.0.0.0:4003 ``` ```shell docker run -p 0.0.0.0:4000-4003:4000-4003 \ -v "$(pwd)/greptimedb_data:/greptimedb_data" \ --name greptime --rm \ greptime/greptimedb:v1.2.1 standalone start \ --http-addr 0.0.0.0:4000 \ --grpc-bind-addr 0.0.0.0:4001 \ --mysql-addr 0.0.0.0:4002 \ --postgres-addr 0.0.0.0:4003 ``` You can also refer to the [Configuration](/user-guide/deployments-administration/configuration.md) document to modify the bind address in the configuration file. ## Next Steps Learn how to write data to GreptimeDB in the [Quick Start](../quick-start.md). --- ## GreptimeDB Installation Overview # Installation Follow these instructions to install GreptimeDB: - [GreptimeDB Standalone](greptimedb-standalone.md) runs as a standalone system in a single process. - [GreptimeDB Cluster](greptimedb-cluster.md) runs as a distributed cluster. ## Check database health After starting GreptimeDB, you can check its status to ensure it is running. ```shell curl http://localhost:4000/health ``` If the GreptimeDB instance is running healthily, you will see the following response: ```json {} ``` ## Next steps - [Quick Start](/getting-started/quick-start.md): Ingest and query data in GreptimeDB using MySQL or PostgreSQL clients. --- ## GreptimeDB Getting Started Overview # Getting Started > **AGENT ONBOARDING:** Read https://docs.greptime.com/SKILL.md and follow the instructions to use GreptimeDB with your AI agent — deploy, configure, ingest, and query. Get started with GreptimeDB quickly by following these steps: - [Installation](./installation/overview.md): Learn how to install GreptimeDB as a standalone or cluster. - [Quick Start](./quick-start.md): Get started with GreptimeDB using your preferred protocols or languages. - [For AI Agents](../faq-and-others/vibecoding.md): Use GreptimeDB with AI agents via the MCP Server, Skills, and machine-readable docs. --- ## Quick Start Before proceeding, please ensure you have [installed GreptimeDB](./installation/overview.md). This guide uses SQL to walk you through GreptimeDB's core capabilities — from ingestion to cross-signal correlation across metrics, logs, and traces. SQL is also GreptimeDB's management interface for creating tables, setting TTL policies, and configuring indexes. :::tip Already running Prometheus, OpenTelemetry, Loki or ES? You can start ingesting data immediately using your existing tools — no schema creation needed (GreptimeDB [creates tables automatically](/user-guide/ingest-data/overview.md#automatic-schema-generation)): - [Prometheus Remote Write](/user-guide/ingest-data/for-observability/prometheus.md) - [OpenTelemetry (OTLP)](/user-guide/ingest-data/for-observability/opentelemetry.md) - [Loki Protocol](/user-guide/ingest-data/for-observability/loki.md) - [Elasticsearch](/user-guide/ingest-data/for-observability/elasticsearch/) Continue with this guide to see what you can do with the data once it's in. ::: **You'll learn (10–15 minutes):** - Connect to GreptimeDB and create metrics, logs, and traces tables - Query and aggregate data with SQL - Search logs by keyword with full-text index - Compute p95 latency in time windows using range queries - **Correlate metrics, logs, and traces in a single query** - Combine SQL and PromQL ## Connect to GreptimeDB GreptimeDB supports [multiple protocols](/user-guide/protocols/overview.md) for interacting with the database. In this guide, we use SQL for simplicity. If your GreptimeDB instance is running on `127.0.0.1` with the MySQL client default port `4002` or the PostgreSQL client default port `4003`, connect using: ```shell mysql -h 127.0.0.1 -P 4002 ``` Or ```shell psql -h 127.0.0.1 -p 4003 -d public ``` You can also use the built-in Dashboard at `http://127.0.0.1:4000/dashboard` to run all the SQL queries in this guide. By default, GreptimeDB does not have [authentication](/user-guide/deployments-administration/authentication/overview.md) enabled. You can connect without providing a username and password. ## Create tables We'll create three tables to simulate a real scenario: gRPC latency metrics, application logs, and request traces. Two application servers, `host1` and `host2`, are recording data. Starting from `2024-07-11 20:00:10`, `host1` begins experiencing issues. ### Metrics table ```sql -- Metrics: gRPC call latency in milliseconds CREATE TABLE grpc_latencies ( ts TIMESTAMP TIME INDEX, host STRING, method_name STRING, latency DOUBLE, PRIMARY KEY (host, method_name) ); ``` - `ts`: Timestamp when the metric was collected (the [time index](/user-guide/concepts/data-model.md)). - `host` and `method_name`: [Tag](/user-guide/concepts/data-model.md) columns identifying the time series. - `latency`: [Field](/user-guide/concepts/data-model.md) column containing the actual measurement. ### Logs table ```sql -- Logs: application error logs CREATE TABLE app_logs ( ts TIMESTAMP TIME INDEX, host STRING, api_path STRING, log_level STRING, log_msg STRING FULLTEXT INDEX WITH('case_sensitive' = 'false'), PRIMARY KEY (host, log_level) ) WITH ('append_mode'='true'); ``` - `log_msg` enables [full-text index](/user-guide/manage-data/data-index.md#full-text-index) for keyword search. - [`append_mode`](/user-guide/deployments-administration/performance-tuning/design-table.md#when-to-use-append-only-tables) optimizes for log workloads (no deduplication overhead). ### Traces table ```sql -- Traces: request spans CREATE TABLE traces ( ts TIMESTAMP TIME INDEX, trace_id STRING SKIPPING INDEX, span_id STRING, parent_span_id STRING, service_name STRING, operation STRING, duration DOUBLE, status_code INT, PRIMARY KEY (service_name) ) WITH ('append_mode'='true'); ``` For high-cardinality `trace_id`s, we have enabled the [skip index](/user-guide/manage-data/data-index.md#skipping-index). :::tip We use SQL to ingest data below, so we create the tables manually. However, GreptimeDB is [schemaless](/user-guide/ingest-data/overview.md#automatic-schema-generation) — when using protocols like OpenTelemetry, Prometheus Remote Write, or InfluxDB Line Protocol, tables are created automatically. ::: ## Write data Let's insert sample data simulating the scenario. Before `20:00:10`, both hosts are normal. After `20:00:10`, `host1` starts experiencing latency spikes. ### Normal period (before 20:00:10) ```sql INSERT INTO grpc_latencies (ts, host, method_name, latency) VALUES ('2024-07-11 20:00:06', 'host1', 'GetUser', 103.0), ('2024-07-11 20:00:06', 'host2', 'GetUser', 113.0), ('2024-07-11 20:00:07', 'host1', 'GetUser', 103.5), ('2024-07-11 20:00:07', 'host2', 'GetUser', 107.0), ('2024-07-11 20:00:08', 'host1', 'GetUser', 104.0), ('2024-07-11 20:00:08', 'host2', 'GetUser', 96.0), ('2024-07-11 20:00:09', 'host1', 'GetUser', 104.5), ('2024-07-11 20:00:09', 'host2', 'GetUser', 114.0); ``` ### Anomalous period (after 20:00:10) `host1`'s latency becomes unstable with spikes up to several thousand milliseconds:
Click to expand INSERT statements ```sql INSERT INTO grpc_latencies (ts, host, method_name, latency) VALUES ('2024-07-11 20:00:10', 'host1', 'GetUser', 150.0), ('2024-07-11 20:00:10', 'host2', 'GetUser', 110.0), ('2024-07-11 20:00:11', 'host1', 'GetUser', 200.0), ('2024-07-11 20:00:11', 'host2', 'GetUser', 102.0), ('2024-07-11 20:00:12', 'host1', 'GetUser', 1000.0), ('2024-07-11 20:00:12', 'host2', 'GetUser', 108.0), ('2024-07-11 20:00:13', 'host1', 'GetUser', 80.0), ('2024-07-11 20:00:13', 'host2', 'GetUser', 111.0), ('2024-07-11 20:00:14', 'host1', 'GetUser', 4200.0), ('2024-07-11 20:00:14', 'host2', 'GetUser', 95.0), ('2024-07-11 20:00:15', 'host1', 'GetUser', 90.0), ('2024-07-11 20:00:15', 'host2', 'GetUser', 115.0), ('2024-07-11 20:00:16', 'host1', 'GetUser', 3000.0), ('2024-07-11 20:00:16', 'host2', 'GetUser', 95.0), ('2024-07-11 20:00:17', 'host1', 'GetUser', 320.0), ('2024-07-11 20:00:17', 'host2', 'GetUser', 115.0), ('2024-07-11 20:00:18', 'host1', 'GetUser', 3500.0), ('2024-07-11 20:00:18', 'host2', 'GetUser', 95.0), ('2024-07-11 20:00:19', 'host1', 'GetUser', 100.0), ('2024-07-11 20:00:19', 'host2', 'GetUser', 115.0), ('2024-07-11 20:00:20', 'host1', 'GetUser', 2500.0), ('2024-07-11 20:00:20', 'host2', 'GetUser', 95.0); ```
### Error logs during the anomaly ```sql INSERT INTO app_logs (ts, host, api_path, log_level, log_msg) VALUES ('2024-07-11 20:00:10', 'host1', '/api/v1/resource', 'ERROR', 'Connection timeout'), ('2024-07-11 20:00:10', 'host1', '/api/v1/billings', 'ERROR', 'Connection timeout'), ('2024-07-11 20:00:11', 'host1', '/api/v1/resource', 'ERROR', 'Database unavailable'), ('2024-07-11 20:00:11', 'host1', '/api/v1/billings', 'ERROR', 'Database unavailable'), ('2024-07-11 20:00:12', 'host1', '/api/v1/resource', 'ERROR', 'Service overload'), ('2024-07-11 20:00:12', 'host1', '/api/v1/billings', 'ERROR', 'Service overload'), ('2024-07-11 20:00:13', 'host1', '/api/v1/resource', 'ERROR', 'Connection reset'), ('2024-07-11 20:00:13', 'host1', '/api/v1/billings', 'ERROR', 'Connection reset'), ('2024-07-11 20:00:14', 'host1', '/api/v1/resource', 'ERROR', 'Timeout'), ('2024-07-11 20:00:14', 'host1', '/api/v1/billings', 'ERROR', 'Timeout'), ('2024-07-11 20:00:15', 'host1', '/api/v1/resource', 'ERROR', 'Disk full'), ('2024-07-11 20:00:15', 'host1', '/api/v1/billings', 'ERROR', 'Disk full'), ('2024-07-11 20:00:16', 'host1', '/api/v1/resource', 'ERROR', 'Network issue'), ('2024-07-11 20:00:16', 'host1', '/api/v1/billings', 'ERROR', 'Network issue'); ``` ### Trace spans during the anomaly ```sql INSERT INTO traces (ts, trace_id, span_id, parent_span_id, service_name, operation, duration, status_code) VALUES ('2024-07-11 20:00:12', 'abc123', 'span1', '', 'host1', 'POST /api/v1/resource', 1050.0, 2), ('2024-07-11 20:00:12', 'abc123', 'span2', 'span1', 'host1', 'GetUser', 1000.0, 2), ('2024-07-11 20:00:14', 'def456', 'span3', '', 'host1', 'POST /api/v1/billings', 4250.0, 2), ('2024-07-11 20:00:14', 'def456', 'span4', 'span3', 'host1', 'CreateBilling', 4200.0, 2), ('2024-07-11 20:00:16', 'ghi789', 'span5', '', 'host1', 'POST /api/v1/resource', 3100.0, 2), ('2024-07-11 20:00:16', 'ghi789', 'span6', 'span5', 'host1', 'GetUser', 3000.0, 2), ('2024-07-11 20:00:12', 'jkl012', 'span7', '', 'host2', 'POST /api/v1/resource', 115.0, 0), ('2024-07-11 20:00:12', 'jkl012', 'span8', 'span7', 'host2', 'GetUser', 108.0, 0); ``` ## Query data ### Filter by tags and time index Query the latency of `host1` after `2024-07-11 20:00:15`: ```sql SELECT * FROM grpc_latencies WHERE host = 'host1' AND ts > '2024-07-11 20:00:15'; ``` ```sql +---------------------+-------+-------------+---------+ | ts | host | method_name | latency | +---------------------+-------+-------------+---------+ | 2024-07-11 20:00:16 | host1 | GetUser | 3000 | | 2024-07-11 20:00:17 | host1 | GetUser | 320 | | 2024-07-11 20:00:18 | host1 | GetUser | 3500 | | 2024-07-11 20:00:19 | host1 | GetUser | 100 | | 2024-07-11 20:00:20 | host1 | GetUser | 2500 | +---------------------+-------+-------------+---------+ 5 rows in set (0.14 sec) ``` Calculate the 95th percentile latency grouped by host: ```sql SELECT host, approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) AS p95_latency FROM grpc_latencies WHERE ts >= '2024-07-11 20:00:10' GROUP BY host; ``` ```sql +-------+-------------------+ | host | p95_latency | +-------+-------------------+ | host1 | 4164.999999999999 | | host2 | 115 | +-------+-------------------+ 2 rows in set (0.11 sec) ``` ### Search logs by keyword The `@@` operator performs [full-text search](/user-guide/logs/fulltext-search.md) on indexed columns: ```sql SELECT * FROM app_logs WHERE lower(log_msg) @@ 'timeout' AND ts > '2024-07-11 20:00:00' ORDER BY ts; ``` ```sql +---------------------+-------+------------------+-----------+--------------------+ | ts | host | api_path | log_level | log_msg | +---------------------+-------+------------------+-----------+--------------------+ | 2024-07-11 20:00:10 | host1 | /api/v1/billings | ERROR | Connection timeout | | 2024-07-11 20:00:10 | host1 | /api/v1/resource | ERROR | Connection timeout | | 2024-07-11 20:00:14 | host1 | /api/v1/billings | ERROR | Timeout | | 2024-07-11 20:00:14 | host1 | /api/v1/resource | ERROR | Timeout | +---------------------+-------+------------------+-----------+--------------------+ ``` ### Range query Use [range queries](/reference/sql/range.md) to calculate the p95 latency in 5-second windows: ```sql SELECT ts, host, approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) RANGE '5s' AS p95_latency FROM grpc_latencies ALIGN '5s' FILL PREV ORDER BY host, ts; ``` ```sql +---------------------+-------+-------------+ | ts | host | p95_latency | +---------------------+-------+-------------+ | 2024-07-11 20:00:05 | host1 | 104.5 | | 2024-07-11 20:00:10 | host1 | 4200 | | 2024-07-11 20:00:15 | host1 | 3500 | | 2024-07-11 20:00:20 | host1 | 2500 | | 2024-07-11 20:00:05 | host2 | 114 | | 2024-07-11 20:00:10 | host2 | 111 | | 2024-07-11 20:00:15 | host2 | 115 | | 2024-07-11 20:00:20 | host2 | 95 | +---------------------+-------+-------------+ 8 rows in set (0.06 sec) ``` Range queries are powerful for time-window aggregation. Read the [manual](/reference/sql/range.md) to learn more. ### Correlate metrics, logs, and traces This is where a unified database shines. One query to correlate p95 latency, error counts, and slow trace spans — across all three signal types: ```sql WITH -- Metrics: per-host p95 latency in 5s windows metrics AS ( SELECT ts, host, approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) RANGE '5s' AS p95_latency FROM grpc_latencies ALIGN '5s' FILL PREV ), -- Logs: per-host ERROR count in 5s windows logs AS ( SELECT ts, host, count(log_msg) RANGE '5s' AS num_errors FROM app_logs WHERE log_level = 'ERROR' ALIGN '5s' ), -- Traces: per-host slow span count in 5s windows slow_traces AS ( SELECT date_bin(INTERVAL '5' seconds, ts) AS ts, service_name AS host, COUNT(*) AS slow_spans, MAX(duration) AS max_span_duration FROM traces WHERE duration > 500 GROUP BY date_bin(INTERVAL '5' seconds, ts), service_name ) SELECT m.ts, m.host, m.p95_latency, COALESCE(l.num_errors, 0) AS num_errors, COALESCE(t.slow_spans, 0) AS slow_spans, t.max_span_duration FROM metrics m LEFT JOIN logs l ON m.host = l.host AND m.ts = l.ts LEFT JOIN slow_traces t ON m.host = t.host AND m.ts = t.ts ORDER BY m.ts, m.host; ``` ```sql +---------------------+-------+-------------+------------+------------+-------------------+ | ts | host | p95_latency | num_errors | slow_spans | max_span_duration | +---------------------+-------+-------------+------------+------------+-------------------+ | 2024-07-11 20:00:05 | host1 | 104.5 | 0 | 0 | NULL | | 2024-07-11 20:00:05 | host2 | 114 | 0 | 0 | NULL | | 2024-07-11 20:00:10 | host1 | 4200 | 10 | 4 | 4250 | | 2024-07-11 20:00:10 | host2 | 111 | 0 | 0 | NULL | | 2024-07-11 20:00:15 | host1 | 3500 | 4 | 2 | 3100 | | 2024-07-11 20:00:15 | host2 | 115 | 0 | 0 | NULL | | 2024-07-11 20:00:20 | host1 | 2500 | 0 | 0 | NULL | | 2024-07-11 20:00:20 | host2 | 95 | 0 | 0 | NULL | +---------------------+-------+-------------+------------+------------+-------------------+ 8 rows in set (0.02 sec) ``` The picture is clear: **during the `20:00:10` – `20:00:15` window, `host1` experienced p95 latency spikes (up to 4200ms), 10 error logs, and 4 slow trace spans (max 4250ms). `host2` was unaffected**. In a traditional three-pillar stack, this correlation requires switching between Prometheus, Loki, and Jaeger. With GreptimeDB, it's one query. ### Query with PromQL GreptimeDB natively supports [PromQL](/user-guide/query-data/promql.md). In the Dashboard, switch to the PromQL tab and run: ```promql quantile_over_time(0.95, grpc_latencies{host!=""}[5s]) ``` You can also query via the Prometheus-compatible HTTP API: ```bash curl -X POST \ -H 'Authorization: Basic ' \ --data-urlencode 'query=quantile_over_time(0.95, grpc_latencies{host!=""}[5s])' \ --data-urlencode 'start=2024-07-11 20:00:00Z' \ --data-urlencode 'end=2024-07-11 20:00:20Z' \ --data-urlencode 'step=15s' \ 'http://localhost:4000/v1/prometheus/api/v1/query_range' ```
Output ```json { "status": "success", "data": { "resultType": "matrix", "result": [ { "metric": { "__name__": "grpc_latencies", "host": "host1", "method_name": "GetUser" }, "values": [ [ 1720728015.0, "3560" ] ] }, { "metric": { "__name__": "grpc_latencies", "host": "host2", "method_name": "GetUser" }, "values": [ [ 1720728015.0, "114.2" ] ] } ] } } ```
### Mix SQL and PromQL Use [TQL](/reference/sql/tql.md) to embed PromQL inside SQL — combining the power of both: ```sql TQL EVAL ('2024-07-11 20:00:00Z', '2024-07-11 20:00:20Z', '15s') quantile_over_time(0.95, grpc_latencies{host!=""}[5s]); ``` ```sql +---------------------+---------------------------------------------------------+-------+-------------+ | ts | prom_quantile_over_time(ts_range,latency,Float64(0.95)) | host | method_name | +---------------------+---------------------------------------------------------+-------+-------------+ | 2024-07-11 20:00:15 | 3560 | host1 | GetUser | | 2024-07-11 20:00:15 | 114.2 | host2 | GetUser | +---------------------+---------------------------------------------------------+-------+-------------+ ``` You can even use PromQL as a CTE in a correlation query: ```sql WITH metrics AS ( TQL EVAL ('2024-07-11 20:00:00Z', '2024-07-11 20:00:20Z', '5s') quantile_over_time(0.95, grpc_latencies{host!=""}[5s]) ), logs AS ( SELECT ts, host, COUNT(log_msg) RANGE '5s' AS num_errors FROM app_logs WHERE log_level = 'ERROR' ALIGN '5s' ) SELECT m.*, COALESCE(l.num_errors, 0) AS num_errors FROM metrics AS m LEFT JOIN logs AS l ON m.host = l.host AND m.ts = l.ts ORDER BY m.ts, m.host; ``` ```sql +---------------------+---------------------------------------------------------+-------+-------------+------------+ | ts | prom_quantile_over_time(ts_range,latency,Float64(0.95)) | host | method_name | num_errors | +---------------------+---------------------------------------------------------+-------+-------------+------------+ | 2024-07-11 20:00:10 | 140.89999999999998 | host1 | GetUser | 10 | | 2024-07-11 20:00:10 | 113.8 | host2 | GetUser | 0 | | 2024-07-11 20:00:15 | 3560 | host1 | GetUser | 4 | | 2024-07-11 20:00:15 | 114.2 | host2 | GetUser | 0 | | 2024-07-11 20:00:20 | 3400 | host1 | GetUser | 0 | | 2024-07-11 20:00:20 | 115 | host2 | GetUser | 0 | +---------------------+---------------------------------------------------------+-------+-------------+------------+ ``` ## GreptimeDB Dashboard GreptimeDB includes a built-in [Dashboard](./installation/greptimedb-dashboard.md) at `http://localhost:4000/dashboard`. Use **Table Query** as the general query entry (SQL and PromQL), or switch to **Metrics Query**, **Logs Query**, and **Traces Query** for signal-specific views. ```sql SELECT * FROM grpc_latencies; ``` ![select gRPC latencies](/select-grpc-latencies.png) ## Next steps **Connect your existing stack:** - [Prometheus Remote Write](/user-guide/ingest-data/for-observability/prometheus.md) — point your Prometheus at GreptimeDB - [OpenTelemetry](/user-guide/ingest-data/for-observability/opentelemetry.md) — configure OTel Collector to send metrics, logs, and traces - [Jaeger](/user-guide/query-data/jaeger.md) — use GreptimeDB as Jaeger's storage backend - [Loki](/user-guide/ingest-data/for-observability/loki.md) — send logs using Loki protocol - [Elasticsearch](/user-guide/ingest-data/for-observability/elasticsearch/) — send logs, traces and events using Elasticsearch `_bulk` API - Find [all ingestion methods](/user-guide/ingest-data/overview/#recommended-data-ingestion-methods). **Visualize and monitor:** - [Grafana integration](/user-guide/integrations/grafana.md) — connect Grafana with SQL or PromQL datasource - [Official Dashboard](/getting-started/installation/greptimedb-dashboard.md) — the embedded dashboard at `http://localhost:4000/dashboard` **Go deeper:** - [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md) — architecture, cost comparison, and how GreptimeDB compares - [Observability 2.0 and wide events](/user-guide/concepts/observability-2.md) — where wide events fit, and what they cost - [Demo scene](https://github.com/GreptimeTeam/demo-scene/) — more hands-on examples - [User Guide](/user-guide/overview.md) — complete reference --- ## Architecture GreptimeDB can run as one standalone process or as a distributed cluster. Standalone deployments use the configured local or object-storage backend. Distributed deployments can use shared object storage to separate persistent data files from compute, allowing compute and storage capacity to be adjusted independently. Object storage is not the only state in the system. WAL records accepted writes, Metasrv manages cluster metadata and Region routes, and local disks can cache remote data. Availability and failover depend on the selected WAL mode, Metasrv deployment, Region placement, shared storage, and available Datanodes. For why real-time monitoring and historical analysis can share this storage and query foundation, see [Why GreptimeDB](./why-greptimedb.md#one-system-for-real-time-monitoring-and-historical-analysis). ## High-level Architecture ![GreptimeDB distributed architecture, showing data paths, control paths, and storage responsibilities.](/greptimedb-distributed-architecture.svg) ## Components Distributed mode has three core components and an optional Flow runtime: - [**Metasrv**](/contributor-guide/metasrv/overview.md): Manages catalogs, schemas, tables, Region routes, procedures, and scheduling metadata. - [**Frontend**](/contributor-guide/frontend/overview.md): Accepts client protocols, authenticates requests, plans distributed queries, and routes reads and writes using Metasrv metadata. - [**Datanode**](/contributor-guide/datanode/overview.md): Hosts Regions, executes reads and writes, records WAL entries, performs compaction, and persists data files to the configured local or object-storage provider. - [**Flownode (optional)**](/contributor-guide/flownode/overview.md): Runs [Flow](/user-guide/flow-computation/overview.md) tasks that continuously compute and materialize derived data in sink tables. In standalone mode, one GreptimeDB process provides these database functions without separate service deployment. ## How It Works The following paths describe distributed mode. Standalone mode runs the same database functions in one process. ### Write Path 1. A client sends data through a supported ingestion protocol. 2. Frontend resolves table and Region routes from Metasrv metadata. 3. Frontend splits the request and forwards rows to the Datanodes that host the target Regions. 4. A Datanode writes to memory and the configured [WAL](/user-guide/deployments-administration/wal/overview.md), then eventually flushes immutable data files to the table's configured [storage provider](./storage-location.md). ### Query Path 1. A client submits SQL, PromQL, or an explicitly supported query API such as the Jaeger-compatible API. 2. Frontend plans the query and dispatches work to Datanodes that host the relevant Regions. 3. Datanodes read in-memory data and persistent files, use local caches when applicable, apply pruning and indexes, and return partial results. 4. Frontend combines the results and returns the response. ### Flow Path (Optional) When Flow is enabled, Frontend mediates its data path. In streaming mode, Frontend mirrors writes to Flownode. In batching mode, Flownode queries source tables and writes materialized results to sink tables through Frontend. Source and sink tables keep their own schema, TTL, indexes, and storage settings. See [Flow Computation](/user-guide/flow-computation/overview.md). For storage responsibilities, see [Storage Location](./storage-location.md). For implementation details, see the [Contributor Guide](/contributor-guide/overview.md). --- ## Data Model ## Model GreptimeDB uses a unified observability data model based on relational tables with a time index and `Tag`, `Timestamp`, and `Field` column semantics. Metrics, logs, traces, and events share this model while remaining in separate tables with workload-specific schemas. ![Metrics, logs, and traces remain in separate tables while sharing schema concepts, storage, and query layers.](/unified-observability-model.svg) Every GreptimeDB table has a name and exactly one time index. Columns have the following roles: - `Tag` columns participate in the primary key and group related rows. For metrics, they usually represent labels that identify a time series. Tags are optional: append-only log and event tables can have no primary-key columns. - The `Timestamp` column is declared as the table's time index. It records event or sample time, helps GreptimeDB organize data by time, and enables efficient time-range queries. - `Field` columns store measurements, log content, trace attributes, or other values. They can use numeric, string, JSON, timestamp, and other supported data types. For tables with primary-key columns, persisted rows are ordered by `(primary key, timestamp)`. Tables can merge rows with the same primary key and timestamp according to their [merge mode](/reference/sql/create.md#create-a-table-with-merge-mode). Append-only tables disable deduplication and, when they have no primary key, order persisted rows by timestamp. GreptimeDB stores table data in immutable Parquet SST files; see [Data Layout in SST Files](/contributor-guide/datanode/storage-engine.md#data-layout-in-sst-files) for details. Schema design affects write amplification, compression, index size, and query pruning. See the [Schema Design Guide](/user-guide/deployments-administration/performance-tuning/design-table.md) before choosing primary-key columns and indexes. ### Metrics The following table stores host resource metrics: ```sql CREATE TABLE IF NOT EXISTS system_metrics ( host STRING, idc STRING, cpu_util DOUBLE, memory_util DOUBLE, disk_util DOUBLE, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(host, idc), TIME INDEX(ts) ); ``` ![The system_metrics table maps host and idc to Tag columns, ts to the Timestamp column and time index, and the remaining measurements to Field columns.](/time-series-data-model.svg) - `host` and `idc` are Tag columns declared by `PRIMARY KEY`. - `ts` is the Timestamp column declared by `TIME INDEX`. - `cpu_util`, `memory_util`, and `disk_util` are Field columns. - With the default [`last_row` merge mode](/reference/sql/create.md#create-a-table-with-merge-mode), queries keep the latest row for each `host`, `idc`, and `ts` combination. See [Prometheus Data Model](/user-guide/ingest-data/for-observability/prometheus.md#data-model) for the mapping between Prometheus metrics and GreptimeDB tables. ### Logs An append-only table is often a better fit for access logs: ```sql CREATE TABLE access_logs ( access_time TIMESTAMP TIME INDEX, remote_addr STRING, http_status STRING, http_method STRING, http_refer STRING, user_agent STRING, request STRING ) WITH ('append_mode' = 'true'); ``` - `access_time` is the Timestamp column. - The table has no Tag columns or primary key. - The remaining columns are Fields. - [`append_mode`](/reference/sql/create.md#create-an-append-only-table) disables deduplication and deletion. It fits immutable log records, but not workloads that need row updates or deletes. - Without a primary key, persisted rows are ordered by `access_time`. See [Create a Table](/user-guide/deployments-administration/manage-data/basic-table-operations.md#create-a-table) and the [CREATE TABLE reference](/reference/sql/create.md) for column-role syntax and table options. ### Traces GreptimeDB accepts OpenTelemetry traces through OTLP/HTTP and maps spans to tables with a time index, trace identifiers, span identifiers, attributes, and duration fields. See the [OTLP Trace Data Model](/user-guide/ingest-data/for-observability/opentelemetry.md#data-model-2). Trace ingestion, storage, and SQL queries are first-class capabilities. GreptimeDB also provides a Jaeger-compatible query API. ## Design Considerations The table model provides several practical properties: - schemas expose types and column roles to the storage and query engines; - SQL can filter, aggregate, and join data across tables; - a row can contain multiple Field columns, avoiding the extra rows required by single-value models; - tables can choose primary keys, merge behavior, append-only mode, indexes, TTL, and storage providers independently; - automatic schema generation can create tables and columns for supported ingestion protocols; - the same Tag, Timestamp, and Field concepts apply to metrics, logs, traces, and wide events without requiring them to share a table. GreptimeDB manages table schemas with SQL. See [Table Management](/user-guide/deployments-administration/manage-data/basic-table-operations.md) and [Automatic Schema Generation](/user-guide/ingest-data/overview.md#automatic-schema-generation). Tables can also carry an optional [table semantic layer](./semantic-layer.md) describing signal identity and ingestion metadata for machine consumers. Read [Observability 2.0 and wide events](./observability-2.md) for one optional way to retain more context in this model. --- ## Common Questions ## How does GreptimeDB handle metrics, logs, and traces? GreptimeDB uses the same columnar engine and the same Tag, Timestamp, and Field column semantics for metrics, logs, and traces. The signals can use separate physical tables with different schemas, indexes, TTL settings, and ingestion paths. All three signal types can be queried with SQL. Metrics also support PromQL, while traces have a separate Jaeger-compatible query API. Flow can continuously derive and materialize aggregates from source tables. See the [Data Model](./data-model.md), [Logs](/user-guide/logs/overview.md), and [Traces](/user-guide/traces/overview.md). ## Does GreptimeDB support updates? Partially. For a table that uses deduplication, writing another row with the same primary key and time index updates Field values according to the table's `merge_mode`. The primary key and time index identify the row and are not updated in place. For tables created with SQL, the default `last_row` mode keeps the latest row. The `last_non_null` mode keeps the latest non-null value for each Field. Auto-created tables can use protocol-specific defaults. Deduplication applies within a Region. GreptimeDB accepts any table column as a partition column, but for a deduplicating table, partition columns should be chosen from the primary key so rows with the same primary key remain in one Region and can be merged correctly. See [Table Sharding](/user-guide/deployments-administration/manage-data/table-sharding.md#partition). Append-only tables disable deduplication, so repeated keys and timestamps create additional rows rather than updates. See [Update Data](/user-guide/manage-data/overview.md#update-data) and the [`merge_mode` reference](/reference/sql/create.md#create-a-table-with-merge-mode). ## Does GreptimeDB support deletion? Yes, for tables that allow deletion. You can delete matching rows with SQL, truncate a table, or expire data with TTL. Append-only tables do not support row deletion. See [Delete Data](/user-guide/manage-data/overview.md#delete-data). ## Can I set TTL or retention policy for different tables or measurements? Yes. TTL can be set at database or table level, and a table-level setting takes precedence. See [Manage Data Retention with TTL Policies](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies). ## What are the compression rates of GreptimeDB? There is no single compression ratio. Results depend on schema width, value distribution, primary-key cardinality, repetition, SST format, indexes, and workload. Indexes can improve filtering while increasing storage and write cost. Measure with representative data and retention settings rather than applying a general ratio. One published edge benchmark provides a concrete but workload-specific result: after writing 10 million TSBS rows on a Qualcomm SA8155P, GreptimeDB Edge used 87 MB and SQLite used 1,686 MB. This result applies to the tested Edge edition, schema, and configuration; it is not a general compression ratio for GreptimeDB deployments. See the [GreptimeDB Edge and SQLite report](https://greptime.com/blogs/2024-08-30-sqlite). For schema and index trade-offs, see the [Schema Design Guide](/user-guide/deployments-administration/performance-tuning/design-table.md) and [Data Indexes](/user-guide/manage-data/data-index.md). ## How does GreptimeDB address the high cardinality issue? High cardinality still has a cost: more distinct primary-key values can increase metadata, index, memory, and query work. GreptimeDB provides several controls: - The `flat` SST format reduces per-series overhead and is the default for new tables. It is recommended for high-cardinality primary keys. The [Flat Format engineering article](https://greptime.com/blogs/2025-12-22-flat-format) explains its memtable and merge-path design and includes benchmark conditions. - Inverted, full-text, and skipping indexes can be added where their selectivity and storage cost fit the query workload. Indexing every column is usually unnecessary. - Append-only tables skip deduplication work when records are immutable and do not need updates or deletes. - Table partitioning distributes Regions across Datanodes in a cluster, but partition design and load distribution still matter. - Schema design should keep identifiers in the primary key only when they are used for grouping, deduplication, or filtering. There is no universal cardinality limit that applies to every schema and deployment. Test expected series counts, write rates, query predicates, and retention together. See [`sst_format`](/reference/sql/create.md#create-a-table-with-sst-format), [Data Indexes](/user-guide/manage-data/data-index.md), and the [Schema Design Guide](/user-guide/deployments-administration/performance-tuning/design-table.md). ## Does GreptimeDB support continuous aggregate or downsampling? Yes. [Flow](/user-guide/flow-computation/overview.md) continuously computes over source-table data as new rows arrive and materializes the results in a sink table. It can implement fixed-window aggregation and downsampling while retaining the source data under its own TTL. ## Can I store data in object storage in the cloud? Yes. Persistent data files can be stored in Amazon S3, Google Cloud Storage, Azure Blob Storage, Alibaba Cloud OSS, and supported S3-compatible services. Object storage is separate from WAL, metadata, and local cache; each has its own durability and recovery role. See [Storage Location](./storage-location.md) and [Storage Options](/user-guide/deployments-administration/configuration.md#storage-options). ## How is GreptimeDB's performance compared to other solutions? Performance depends on workload, schema, indexes, retention, hardware, object storage, cache, concurrency, and query shape. Use benchmark results only with their test conditions and compare them with your own expected workload. Published reports include: - [GreptimeDB vs. InfluxDB](https://greptime.com/blogs/2024-08-07-performance-benchmark) - [GreptimeDB vs. TimescaleDB](https://greptime.com/blogs/2025-12-09-greptimedb-vs-timescaledb-benchmark) - [GreptimeDB vs. Grafana Mimir](https://greptime.com/blogs/2024-08-02-datanode-benchmark) - [JSONBench: one billion JSON documents](https://greptime.com/blogs/2025-03-18-jsonbench-greptimedb-performance) — records the third-party benchmark result and reproduction steps - [Log workload: GreptimeDB, ClickHouse, and Elasticsearch](https://greptime.com/blogs/2025-03-10-log-benchmark-greptimedb) - [GreptimeDB vs. Loki](https://greptime.com/blogs/2025-08-07-beyond-loki-greptimedb-log-scenario-performance-report) - [GreptimeDB Edge vs. SQLite on Qualcomm SA8155P](https://greptime.com/blogs/2024-08-30-sqlite) ## Does GreptimeDB have disaster recovery solutions? GreptimeDB provides the components needed to build a recovery plan, but object storage alone is not a complete solution. Persistent data files, WAL, Metasrv metadata, Region placement and failover, and deployment configuration each affect recovery point and recovery time objectives. Cluster failover also depends on a correctly configured topology and healthy replacement Datanodes. Back up and test metadata and WAL recovery alongside object-store policies. See [Disaster Recovery](/user-guide/deployments-administration/disaster-recovery/overview.md), [WAL Overview](/user-guide/deployments-administration/wal/overview.md), and [Storage Location](./storage-location.md). ## Does GreptimeDB support geospatial computation? Yes. GreptimeDB provides SQL functions for WKT values, Geohash, H3, S2, spatial relationships, distance, and area. These functions do not create or imply a separate database spatial index. See [Geospatial Functions](/reference/sql/functions/geo.md). ## Any JSON support? GreptimeDB provides two JSON column types: - The experimental [`JSON`](/reference/sql/data-types.md#json-type-experimental) type stores general JSON values and works with [JSON functions](/reference/sql/functions/json.md). - [`JSON2`](/user-guide/logs/json2.md), available since v1.2 and currently Beta, stores subpaths in a structured columnar form for logs and other semi-structured data. It supports dot-path access, `json_get`, and optional type hints. JSON2 currently requires an append-only table. Neither type currently provides index options for JSON subpaths. Put stable, frequently filtered attributes in typed columns when they need inverted, full-text, or skipping indexes. See [Data Indexes](/user-guide/manage-data/data-index.md). ## More Questions? For deployment, migration, operations, and schema questions, see the full [FAQ](/faq-and-others/faq.md). --- ## Key Concepts ## Database A database is a namespace for tables and other objects. It groups data for management and access, but is not by itself a complete tenant-isolation boundary. ## Time-Series Table A GreptimeDB table follows a relational schema and has exactly one time index. Columns use Tag, Timestamp, and Field semantics. Time-series tables are a natural fit for metrics and IoT workloads, while logs, traces, and event data use the same table model with schemas and options suited to those signals. Tables can be created with SQL or automatically from supported ingestion protocols. In distributed deployments, a table can be partitioned into Regions placed on different Datanodes. See [Data Model](./data-model.md). ## Time Index and Primary Key Every GreptimeDB table has exactly one time index. It records sample or event time and lets the storage engine organize and prune data by time. Primary-key columns use Tag semantics and identify a series or record group. In tables that use deduplication, the primary key and time index identify rows that are merged according to the table's [`merge_mode`](/reference/sql/create.md#create-a-table-with-merge-mode). This is not a general relational uniqueness constraint: append-only tables can omit the primary key, and append-only mode keeps repeated keys and timestamps as separate rows. ## Table Engine A table engine controls how table data is written, organized, compacted, and read. The main engines are: - **Mito Engine**: The general-purpose engine for time-indexed tables, including logs, traces, and event data. - **Metric Engine**: Built on Mito Engine and optimized for large numbers of Prometheus-style metric tables. User-facing metric tables remain logical tables and can share physical tables and metadata internally. Table engines are separate from storage providers such as local files, Amazon S3, or Google Cloud Storage. See [Table Engines](/reference/about-greptimedb-engines.md) and [Storage Location](./storage-location.md). ## Region A Region is a physical partition of a table and the basic unit of storage, scheduling, and migration. A Region is hosted by a Datanode, while Metasrv maintains its route and placement metadata. Clients normally access tables through Frontend without addressing Regions directly. ## Data Types GreptimeDB columns are strongly typed. Automatic schema generation can create tables and add compatible columns, but values written to an existing column must match or be cast to its data type. See [Data Types](/reference/sql/data-types.md). ## Index An index is an optional data structure used to accelerate selected query patterns. GreptimeDB provides inverted, full-text, and skipping indexes with different selectivity, storage, and write-cost trade-offs. See [Data Indexes](/user-guide/manage-data/data-index.md). ## View A view is a named SQL query presented as a virtual table. It stores the query definition, not a materialized copy of its result; querying the view reads the underlying tables. ## Pipeline Pipeline processes data during ingestion. It can parse, transform, enrich, and route incoming records before storing them in tables. See [Pipeline](/user-guide/logs/use-custom-pipelines.md). ## Flow A Flow is a continuous computation over incoming rows after they reach source tables. It updates and materializes its result in a sink table, which can be queried and managed like other tables. See [Flow Computation](/user-guide/flow-computation/overview.md). --- ## Observability 2.0 Observability 2.0 is an industry term for an approach to telemetry, not a product category. It usually refers to retaining wide events and analyzing them without deciding every question in advance. GreptimeDB's [data model](./data-model.md) supports native metrics, logs, and traces alongside wide events. This page focuses on the wide-event approach associated with Observability 2.0. GreptimeDB supports this practice but does not require it; metrics, logs, and traces remain first-class capabilities. ## The Limits of Three Pillars Metrics, logs, and traces remain useful abstractions. The problem is not the signals themselves, but the operational boundary that often forms around each one: 1. **Separate context**: When signals are stored and queried in separate systems, correlating an alert with the relevant logs and traces takes extra work. 2. **Questions fixed at collection time**: Pre-aggregated metrics answer known questions efficiently, but cannot recover dimensions that were not recorded. 3. **Lost structure**: Plain-text logs often contain useful fields that are expensive to parse and index later. Shared schema concepts, storage infrastructure, and query tools reduce these boundaries. Wide events are one way to retain more context, not a replacement for every metric, log, or trace. ## Wide Events A wide event is a structured record with many fields describing one operation or business event. It can include high-cardinality values such as user IDs, session IDs, trace IDs, and request attributes. ### What is a Wide Event? For example, an event for a POST request might include user and subscription data, database and cache operations, HTTP attributes, outcome, and duration: ```json { "timestamp": "2026-08-12T08:15:30Z", "method": "POST", "path": "/articles", "service": "articles", "outcome": "ok", "status_code": 201, "duration": 268, "user": { "id": "fdc4ddd4-8b30-4ee9-83aa-abd2e59e9603", "subscription": { "plan": "free", "trial": true } }, "db": { "query": "INSERT INTO articles (...)" }, "cache": { "operation": "write" }, "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736" } ``` Capture only the context that is useful and safe to retain. Credentials, personal data, query parameters, prompts, and request bodies may require filtering or redaction before ingestion. ### Views Derived from Wide Events In this approach, a wide event can produce several views: - a metric aggregated by status and time window; - a searchable log record containing the event details; - a trace or span view linked by trace and span IDs. This is a useful mental model, not a requirement that every signal must be reconstructed from raw events. Native metrics are often the better representation for fixed aggregations, and standard trace data remains useful for call graphs and latency analysis. ## AI and the Need for Fine-Grained Observability AI applications often need to relate model requests, responses, tool calls, latency, token use, evaluations, and application state. A structured event can keep this context queryable when the instrumentation captures it. The same trade-offs apply as in other workloads: prompts and responses can be large or sensitive, session identifiers create high cardinality, and incomplete instrumentation produces incomplete context. Teams should choose fields, retention periods, and redaction rules based on the questions they need to answer. The [table semantic layer](./semantic-layer.md) can describe what each table represents so that agents and tools do not have to infer signal type, source, or metric type from column names. ## How GreptimeDB Supports Wide Events GreptimeDB stores wide events in regular time-indexed tables. Pipeline, SQL, and Flow support different stages of the approach: | Stage | GreptimeDB capability | Role | | --- | --- | --- | | Ingest, when needed | [Pipeline](/user-guide/logs/use-custom-pipelines.md) | Parse, transform, and enrich incoming logs before storage. | | Store and analyze | Time-indexed tables and SQL | Retain wide events for detailed and retrospective analysis. | | Derive | [Flow](/user-guide/flow-computation/overview.md) | Continuously aggregate incoming rows into separate metrics tables. | Wide events do not require native metrics, logs, traces, and raw events to share one table. Each can use separate tables with different schemas, retention policies, and indexes. Pipeline and Flow handle different stages. Pipeline parses, transforms, and enriches logs during ingestion. Its output is structured, multi-column data; when those fields retain the context of an operation or business event, each row can serve as a wide event. Flow can continuously aggregate the same event rows as they arrive into metrics tables for dashboards and alerts. ![Pipeline can process logs during ingestion, while Flow can aggregate incoming event rows into a derived metrics table.](/optional-pipeline-flow.svg) For example, Flow can derive a status metric from an event table: ```sql CREATE FLOW http_status_count SINK TO status_metrics AS SELECT status_code, COUNT(*) AS count, date_bin('1 minute'::INTERVAL, timestamp) AS time_window FROM access_logs GROUP BY status_code, time_window; ``` The stored events remain available for detailed SQL queries, while the sink table serves fixed dashboards and alerts efficiently. ## Trade-offs The wide-event approach changes where you pay for flexibility: - **Wider events increase data volume.** More fields and repeated context consume ingestion bandwidth and storage, even with columnar compression. - **High cardinality and long retention increase cost.** Keep only useful dimensions, and set retention independently for raw and derived data. - **Complete context depends on instrumentation quality.** Missing identifiers, inconsistent schemas, or poor propagation cannot be repaired by the database. - **Native metrics still fit fixed aggregations.** Counters, gauges, histograms, recording rules, dashboards, and alerts usually do not need a raw event behind every sample. Schema governance, sampling, redaction, and retention are part of the design. A wide event should be as wide as the investigation requires, not as wide as the application can produce. ## Adoption Paths ### Keep Native Signals and Unify Storage and Query Continue using existing ingestion protocols. Query metrics with PromQL, query all signal types with SQL, and use the Jaeger-compatible API for traces. Store each signal in separate GreptimeDB tables, and use shared identifiers and SQL when cross-signal analysis is needed. This path minimizes instrumentation and dashboard changes. Start with [Prometheus](/user-guide/ingest-data/for-observability/prometheus.md), [logs](/user-guide/logs/overview.md), [OpenTelemetry](/user-guide/ingest-data/for-observability/opentelemetry.md), or [traces](/user-guide/traces/overview.md). ### Add Raw Events Where Full Context Matters Instrument selected business operations or AI workflows as structured events. If the context starts in logs, use [Pipeline](/user-guide/logs/use-custom-pipelines.md) to extract and transform it into structured columns during ingestion. Keep the event table for retrospective analysis, and use [Flow](/user-guide/flow-computation/overview.md) to derive metrics for known dashboards and alerts. This path provides more context at the cost of higher data volume and stricter schema and retention management. The two paths can coexist. Adopt raw events only where the additional context justifies their cost. ## Further Reading - [Observability 2.0 and the Database for It](https://greptime.com/blogs/2025-04-25-greptimedb-observability2-new-database) — An earlier description of the wide-event approach - [Unified Storage for Observability - GreptimeDB's Approach](https://greptime.com/blogs/2024-12-24-observability) — GreptimeDB's unified storage model - [Agent Observability: Can the Old Playbook Handle the New Game?](https://greptime.com/blogs/2025-12-11-agent-observability) — Context needed for AI application debugging - [Scaling Observability at Poizon](https://greptime.com/blogs/2025-05-06-poizon-observability-greptimedb-monitoring-use-case) — Raw events and continuous aggregation in production --- ## GreptimeDB Concepts Overview # Concepts GreptimeDB is an open-source observability database. It uses one columnar engine to process metrics, logs, and traces, and supports scalable deployments backed by object storage. The signals share Tag, Timestamp, and Field column semantics, but can remain in separate tables with different schemas and retention policies. **Start here:** - [Why GreptimeDB](./why-greptimedb.md) — Product scope, unified processing, scaling, protocol boundaries, and deployment options - [Data Model](./data-model.md) — Shared Tag, Timestamp, and Field semantics for metrics, logs, traces, and event data - [Architecture](./architecture.md) — Standalone and distributed deployments, compute-storage separation, and component responsibilities **Deep dives:** - [Observability 2.0 and wide events](./observability-2.md) — The role and trade-offs of wide events alongside native signals - [Table Semantic Layer](./semantic-layer.md) — Optional metadata that tells machine consumers what observability concept each table represents - [Storage Location](./storage-location.md) — Local storage, object storage, and per-table storage providers - [Key Concepts](./key-concepts.md) — Tables, Regions, time index, data types, views, and Flow - [Common Questions](./features-that-you-concern.md) — FAQ on updates, deletion, TTL, compression, high cardinality, and other technical boundaries ## Further Reading - [Observability 2.0 and the Database for It](https://greptime.com/blogs/2025-04-25-greptimedb-observability2-new-database) — An earlier description of the wide-event approach - [Unifying Logs and Metrics](https://greptime.com/blogs/2024-06-25-logs-and-metrics) - [GreptimeDB Storage Engine Design](https://greptime.com/blogs/2022-12-21-storage-engine-design) --- ## Table Semantic Layer (Experimental) :::warning The semantic layer is experimental and may change in future releases. Tables without semantic metadata keep working unchanged; the layer is optional and additive. ::: The semantic layer stores metadata on a table so machine consumers such as LLM agents, alert and dashboard builders, [MCP servers](/user-guide/integrations/mcp.md), and ETL pipelines can identify what the table represents without inferring it from column names. ## Why it exists GreptimeDB ingests OTLP metrics, traces, and logs, plus Prometheus remote write, InfluxDB Line Protocol, OpenTSDB, Loki Push API, and Elasticsearch Bulk API data. Some protocols, especially OpenTelemetry, carry semantic metadata that is not represented by ordinary table columns. The amount and quality of metadata varies by protocol: - An OTLP traces table looks like any other wide table; signal type and source must be guessed from naming. - An OTLP metric's unit (`s`, `By`) is discarded by the row encoders and is unrecoverable from the data. - OTLP aggregation temporality (`cumulative` vs `delta`) is invisible in the metric name. - A Prometheus `counter` inferred from a `_total` suffix is not a protocol declaration. Without semantic metadata, the table does not record that distinction. This metadata is available during ingestion but is not represented in ordinary data rows. Keeping it allows an alert generator to distinguish a rate from an absolute value, a dashboard builder to select a visualization by signal type, and an agent to inspect a structured catalog instead of inferring meaning from column names. ## How it works The layer uses existing SQL interfaces. It does not add a protocol or DDL keyword. It has three parts: 1. **`greptime.semantic.*` table options** store table identity and ingestion metadata alongside options such as `ttl` and `table_data_model`. 2. **Column `COMMENT`** stores additional column-level information through standard SQL. 3. **[`information_schema.table_semantics`](/reference/sql/information-schema/table-semantics.md)** is the discovery view. It returns one row for each table with at least one `greptime.semantic.*` option. ## Vocabulary All keys are flat strings under the `greptime.semantic.` prefix, and all values are strings. The vocabulary contains only metadata that a consumer cannot readily recover from the schema, columns, or metric naming conventions. It omits constants, values already encoded in a metric name such as the Prometheus `_total` suffix, and information that only repeats a column. The whitelist is closed: an unrecognized key under the prefix (such as `greptime.semantic.future.key`) or an out-of-domain value is rejected. ### Common keys (all signals) | Key | Description | Example values | | --- | --- | --- | | `greptime.semantic.signal_type` | The telemetry signal the table represents. | `metric` / `trace` / `log` / `event` / `unknown` | | `greptime.semantic.source` | The ingestion ecosystem that wrote the data. | `opentelemetry` / `prometheus` / `influxdb` / `opentsdb` / `loki` / `elasticsearch` / `custom` / `mixed` / `unknown` | | `greptime.semantic.pipeline` | The internal ingestion data model. The signal-agnostic successor to `table_data_model`. | `greptime_trace_v1` | ### Trace keys | Key | Description | Example values | | --- | --- | --- | | `greptime.semantic.trace.conventions` | The semantic-conventions version the rows conform to, typically an OTel schema URL. | `https://opentelemetry.io/schemas/1.27.0` / `mixed` / `unknown` | ### Metric keys | Key | Description | Example values | | --- | --- | --- | | `greptime.semantic.metric.type` | The instrument kind. | `counter` / `gauge` / `histogram` / `summary` / `updown_counter` / `gauge_histogram` / `info` / `stateset` / `mixed` / `unknown` | | `greptime.semantic.metric.unit` | The unit in [UCUM](https://ucum.org/) notation. Discarded by the row encoders, so unrecoverable once ingested. | `s` / `By` / `{request}` | | `greptime.semantic.metric.temporality` | Aggregation temporality (OTLP only). Invisible in the metric name. | `cumulative` / `delta` / `mixed` / `unknown` | | `greptime.semantic.metric.metadata_quality` | How the metric type was obtained and how reliable `metric.type` is. | `declared` (the protocol stated it) / `inferred` (derived from a name suffix) / `unknown` | | `greptime.semantic.metric.original_name` | The original OpenTelemetry name, recorded when the table name is converted to Prometheus naming. | `http.server.duration` | Consumers should verify an `inferred` counter before applying `rate()` semantics because the type was derived from its name rather than declared by the protocol. `unknown` and `mixed` are shared sentinel values. `unknown` means the value could not be determined when the option was set. `mixed` means a single-valued key received conflicting values over the table's lifetime, for example when a table received rows from more than one source. Treat a single-valued semantic key as descriptive metadata, not an enforced constraint. ## Automatic stamping on ingestion Supported auto-create paths stamp identity (`signal_type` + `source`). OTLP metrics also carry metric type, unit, and temporality because the OTLP wire format declares them; OTLP traces carry pipeline and convention metadata. Other protocols generally provide only identity metadata, as shown below. | Ingestion path | `signal_type` | `source` | Additional keys | | --- | --- | --- | --- | | OTLP metrics | `metric` | `opentelemetry` | `metric.type`, `metric.unit`, `metric.temporality`, `metric.metadata_quality` = `declared`, `metric.original_name` | | OTLP traces | `trace` | `opentelemetry` | `pipeline` = `greptime_trace_v1`, `trace.conventions` | | OTLP logs | `log` | `opentelemetry` | — | | Prometheus remote write | `metric` | `prometheus` | identity only | | InfluxDB line protocol | `metric` | `influxdb` | identity only | | OpenTSDB | `metric` | `opentsdb` | identity only | | Loki | `log` | `loki` | identity only | | Elasticsearch Bulk API | `log` | `elasticsearch` | identity only | Semantic options are set when the table is created and are not updated by later writes. For example, later writes do not change `metadata_quality` from `inferred` to `declared` or revise `trace.conventions`. ## Manual tagging with DDL You can set the same options yourself in `CREATE TABLE ... WITH (...)`. Only whitelisted keys with a valid value are accepted: ```sql CREATE TABLE my_metrics ( ts TIMESTAMP TIME INDEX, val DOUBLE ) WITH ( 'greptime.semantic.signal_type' = 'metric', 'greptime.semantic.source' = 'custom', 'greptime.semantic.metric.type' = 'counter', 'greptime.semantic.metric.unit' = 'By' ); ``` The options appear in `SHOW CREATE TABLE` output and in the `table_semantics` view. ## Discovering semantic metadata A consumer can list all tables with semantic metadata: ```sql SELECT table_schema, table_name, signal_type, source, pipeline, metadata_quality, semantic_options FROM information_schema.table_semantics ORDER BY table_name; ``` `signal_type`, `source`, `pipeline`, and `metadata_quality` are promoted to dedicated columns; the remaining signal-specific keys are folded into the `semantic_options` JSON string (with the `greptime.semantic.` prefix stripped). See the [`TABLE_SEMANTICS`](/reference/sql/information-schema/table-semantics.md) reference for the full schema and more examples. The [GreptimeDB MCP Server](/user-guide/integrations/mcp.md) reads this view so AI assistants can identify tables from metadata instead of manual descriptions. --- ## Storage Location GreptimeDB can keep persistent data files on a local file system or in object storage. Local storage is the default fit for standalone deployments. Shared object storage is commonly used by distributed deployments that need compute-storage separation. Storage location is separate from table engine selection. [Mito Engine and Metric Engine](/reference/about-greptimedb-engines.md) define how table data is organized and processed; local file systems, Amazon S3, Google Cloud Storage, and Azure Blob Storage are storage providers used to persist the resulting data files. Metric Engine is built on Mito Engine and uses its storage capabilities. ## Local and Object Storage With local storage, persistent data files are placed under the configured `data_home`. This is simple for standalone deployments, but the files are tied to that host and must be included in its backup and recovery plan. With object storage, persistent data files are placed in a configured bucket or container. Datanodes can use local disks as cache while object storage remains the shared persistent location. Object storage makes it possible to scale compute separately from storage, but query latency and cost still depend on cache behavior, network performance, request volume, and provider pricing. ## Storage Responsibilities A GreptimeDB deployment contains several kinds of state: | State | Responsibility | Typical location | Durability and recovery | | --- | --- | --- | --- | | Persistent data files | Table data, SST files, and persistent indexes | Local file system or object storage | Protect local files with backups. For object storage, configure retention, versioning, and replication to match recovery requirements. | | WAL | Records accepted writes before they are represented in persistent data files | Local WAL, remote WAL, or Noop WAL, depending on configuration | Protects accepted but unflushed writes. Recovery depends on the selected WAL mode; Noop WAL does not retain these writes. | | Metadata | Catalogs, schemas, table definitions, Region routes, and procedure state | Local metadata in standalone mode; Metasrv state in cluster mode | Required to reconstruct database state. Persist, replicate, or back it up according to the deployment mode. | | Local cache | Cached object-storage data and temporary index data | Datanode or standalone local disk | Disposable and rebuildable from persistent data. It should not be part of the durability boundary. | | Process logs | Operational logs from GreptimeDB components | Local logging destination or configured log collector | Not required for database recovery. Retain them according to operational and compliance requirements. | These requirements are deployment-dependent. See the recovery documentation below before assigning an RPO or RTO. ## Object Storage and Disaster Recovery Putting persistent data files in object storage does not by itself provide a complete disaster-recovery plan. Recovery also depends on the selected WAL mode, metadata backup or replication, Region state, object-store versioning and retention, and deployment configuration. For recovery planning, see [Disaster Recovery](/user-guide/deployments-administration/disaster-recovery/overview.md) and the [WAL Overview](/user-guide/deployments-administration/wal/overview.md). Disabling WAL with Noop WAL changes the durability boundary and should be evaluated separately. ## Supported Storage Providers GreptimeDB supports: - local file storage; - Amazon S3 and S3-compatible services, including MinIO, DigitalOcean Spaces, Tencent Cloud Object Storage, and Baidu Object Storage; - Google Cloud Storage; - Azure Blob Storage; - Alibaba Cloud OSS. See [Storage Options](/user-guide/deployments-administration/configuration.md#storage-options) for the complete configuration keys and current provider list. ## Multiple Storage Providers An administrator can configure multiple storage providers under `[[storage.providers]]`. A table can then select one configured provider through its `storage` table option: ```sql CREATE TABLE archive_events ( ts TIMESTAMP TIME INDEX, payload STRING ) WITH (storage = 'archive_s3'); ``` This setting chooses where the table engine stores persistent files. It does not select a different table engine. Provider names and credentials must be configured before the table is created. See [Create a Table with Custom Storage](/reference/sql/create.md#create-a-table-with-custom-storage). --- ## Why GreptimeDB Teams usually evaluate a new observability backend when separate signal stores, storage expansion, or long-term analysis have become too expensive to operate. GreptimeDB addresses these problems with one columnar engine for metrics, logs, and traces, while keeping the data model and deployment choices explicit. ## What GreptimeDB Is GreptimeDB is an open-source observability database. It stores and queries metrics, logs, and traces with one columnar engine and a shared SQL layer. It can run as a standalone process on local storage or as a distributed cluster backed by object storage. GreptimeDB is not a general-purpose transactional database. Its storage, indexing, retention, and query paths are designed for append-heavy, time-indexed workloads such as observability and IoT data. ## Why One Engine for Three Signals When metrics, logs, and traces live in separate databases, each store brings its own capacity model, lifecycle policy, query path, and failure modes. Correlation also depends on moving data between systems or coordinating several queries during an incident. GreptimeDB uses the same columnar engine and [Tag, Timestamp, and Field semantics](./data-model.md) across the three signal types. This gives teams: - one storage and lifecycle-management system for metrics, logs, and traces; - SQL across all signal tables and PromQL for metrics; - [Flow](/user-guide/flow-computation/overview.md) for continuous aggregation and materialized derived data; - SQL correlation through common identifiers when instrumentation records them. Unification happens at the engine, storage, and query layers. It does not require metrics, logs, and traces to use one table, one schema, or the [wide-event model](./observability-2.md). ## One Columnar Engine, Separate Tables Reducing the number of systems should not force unlike signals into the same schema. Metric samples, log records, spans, and wide events have different access patterns and retention needs. GreptimeDB lets each workload use tables suited to its data: - metrics commonly use primary-key columns for labels and PromQL for queries; - logs often use append-only tables and text or inverted indexes selected for the workload; - traces retain trace and span identifiers and can be queried with SQL or the Jaeger-compatible API; - wide events can retain more context where retrospective analysis justifies the added storage cost. These tables can have different schemas, indexes, TTL settings, compaction options, and storage providers. See [Data Model](./data-model.md) and [Storage Location](./storage-location.md) for the underlying mechanisms. ## One System for Real-Time Monitoring and Historical Analysis Incident response needs fast queries over recent data. Trend analysis, capacity work, and investigations may scan weeks or months. Splitting these workloads between a monitoring backend and an analytics database adds another ingestion path, another copy of the data, and another system to operate. In distributed deployments backed by object storage, GreptimeDB handles both workloads with the same engine, storage system, and query layer. Recent data can be served from memory and local caches, while persistent data remains in object storage for longer-range queries. Historical analysis does not require a separate analytics database and data-copy pipeline. ![In a distributed deployment backed by object storage, real-time monitoring and historical analysis have different workload profiles but share GreptimeDB's engine, storage system, and query layer.](/shared-system-realtime-historical.svg) ## Object Storage and Independent Scaling When persistent files are tied to compute nodes, adding capacity can require moving data, rebalancing local disks, or provisioning storage and compute together. That makes a storage-heavy observability cluster harder to change as retention grows. In distributed GreptimeDB deployments configured with shared object storage, persistent data files are kept in services such as Amazon S3, Google Cloud Storage, or Azure Blob Storage. Datanodes perform writes, compaction, and queries, while local disks can cache remote data. Compute and object-storage capacity can be adjusted separately without copying every persistent data file between Datanodes. See [Architecture](./architecture.md) and [Storage Location](./storage-location.md). ## Protocols and Query Boundaries Replacing collection agents, dashboards, and client code can cost more than deploying a new database. GreptimeDB therefore accepts data through several established protocols: - Prometheus Remote Write for metrics; - OpenTelemetry OTLP/HTTP for metrics, logs, and traces; - Loki Push API for logs; - Elasticsearch Bulk API for document ingestion; - InfluxDB Line Protocol, MySQL, PostgreSQL, and the GreptimeDB gRPC and HTTP APIs. These integrations cover specific ingestion or client interfaces, not every feature of the source system. Loki ingestion does not provide LogQL, and the open-source Elasticsearch integration covers the Bulk API rather than the full Query DSL. Use [SQL](/user-guide/query-data/sql.md) across signal types, [PromQL](/user-guide/query-data/promql.md) for metrics, and the Jaeger-compatible query API for traces. Review each protocol page before planning a migration. ## Open-Source and Enterprise Scope Edition boundaries matter when evaluating scale, availability, and operating effort. The open-source project includes standalone and cluster deployment, object-storage support, SQL and PromQL, Flow, indexing, and the ingestion interfaces listed above. [GreptimeDB Enterprise](/enterprise/overview.md) adds separately documented capabilities such as read replicas, workload isolation through Datanode groups, automatic Region balancing and repartitioning, RBAC, LDAP integration, audit logging, and enterprise disaster-recovery options. These capabilities are not implied when this guide describes the open-source cluster. ## Configuration Choices Using one system changes the controls you operate; it does not make the workloads identical. You can tune each workload through these controls: - [Table design](/user-guide/deployments-administration/performance-tuning/design-table.md): choose schemas, primary keys, indexes, append-only behavior, and partitioning. Set retention with [TTL policies](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies) and tune [compaction](/user-guide/deployments-administration/manage-data/compaction.md) when the workload needs it. - [Capacity planning](/user-guide/deployments-administration/capacity-plan.md): size compute, memory, and local cache for the expected ingestion rate and mix of recent and historical queries. Use the [performance tuning guide](/user-guide/deployments-administration/performance-tuning/performance-tuning-tips.md) to adjust cache and query behavior from runtime evidence. - [Durability and recovery](/user-guide/deployments-administration/disaster-recovery/overview.md): select a [WAL mode](/user-guide/deployments-administration/wal/overview.md), metadata storage, object-storage policy, and backup and restore process that meet the deployment's RPO and RTO. - [Region operations](/user-guide/deployments-administration/manage-data/overview.md): plan table sharding, manual Region migration, and failover for distributed deployments. Enterprise deployments can also use [Datanode groups](/enterprise/deployments-administration/deploy-on-kubernetes/configure-datanode-groups.md) for workload isolation and [Region Balancer](/enterprise/autopilot/region-balancer.md) for automatic balancing. The right settings depend on the workload and deployment mode; they are not a single preset shared by every signal. ## What Production Users Report Published case studies give results for specific workloads and configurations: - [OceanBase Cloud](https://greptime.com/blogs/2025-07-22-user-case-obcloud-log-management-greptimedb) runs 80+ GreptimeDB clusters with 300 TB of log and SQL audit data under a seven-day retention period. The case study reports around 1 GB/s of sustained writes and 60%+ lower overall log storage cost after moving from Loki. - [Poizon](https://greptime.com/blogs/2025-05-06-poizon-observability-greptimedb-monitoring-use-case) uses Flow to maintain 10-second, 1-minute, and 10-minute rollups from detailed events. Its case study reports that pre-aggregation reduced P99 query latency from seconds to milliseconds. Results depend on schema, indexes, retention, hardware, object-storage pricing, cache configuration, and query workload. Use published [benchmark reports](https://greptime.com/blogs/2024-09-09-report-summary) with their test conditions when sizing a deployment. ## Agent-Driven Investigation [Agent RCA Bench](https://rca-bench.greptime.com/) had six models investigate the same 14 incidents through the native Prometheus, Loki, and Tempo query APIs, and through read-only SQL and PromQL over GreptimeDB tables. Measured against the three separate backends, the GreptimeDB interface produced: - **40% fewer wrong diagnoses** — 38 wrong answers in 168 investigations against 63, or 130 correct root causes against 105. - **48% fewer input tokens read** — 199.8 million against 381.8 million. All six models read fewer tokens, and five of six were more accurate. - **About 45% lower cost to run** — USD 98–101 against USD 184–187 for the full set of investigations. The comparison changes the store, the query languages, and the tool surface together; it measures interface bundles, not the storage engine alone. The [report](https://github.com/GreptimeTeam/agent-rca-bench/blob/main/REPORT.md) publishes the protocol, the per-run artifacts, and the scope of each claim. ## Compare Against Your Current Stack The useful comparison is usually against the systems already carrying the workload: Prometheus, Mimir, or Thanos for metrics; Loki or Tempo for logs and traces; Elasticsearch; VictoriaMetrics; or ClickHouse and ClickStack. The [GreptimeDB comparison hub](https://greptime.com/compare/) links to product-specific pages covering architecture, protocol and query differences, migration paths, and benchmark conditions. Use the page for the system you run today rather than a context-free feature matrix. ## Choose a Next Step - Run the [Quick Start](/getting-started/quick-start.md) to test ingestion and queries locally. - Review the [product comparisons](https://greptime.com/compare/) against your current stack. - Use the [migration guides](/user-guide/migrate-to-greptimedb/overview.md) to check protocol, query, dashboard, and historical-data changes before a production cutover. --- ## Authentication Authentication occurs when a user attempts to connect to the database. In GreptimeDB, users are authenticated by "user provider"s. There are various implementations of user providers in GreptimeDB: - [Static User Provider](./static.md): A simple built-in user provider implementation that finds users from a static file. - [LDAP User Provider](/enterprise/deployments-administration/authentication.md): **Enterprise feature.** A user provider implementation that authenticates users against an external LDAP server. --- ## Static User Provider GreptimeDB supports username/password authentication with `static_user_provider`, which loads credentials from a file or a command-line argument at startup. `watch_file_user_provider` uses the same file format and reloads credentials when the file changes. ## Standalone Mode GreptimeDB reads the user configuration from a file where each line defines a user with their password and optional permission mode. ### Basic Configuration The basic format uses `=` as a separator between username and password: ``` greptime_user=greptime_pwd alice=aaa bob=bbb ``` Users configured this way have read-write access by default. File parsing follows these rules: - Blank lines and lines starting with `#` are ignored after trimming leading and trailing whitespace from each line. - Each credential must contain exactly one `=`. Plaintext passwords containing `=` are not supported, including with the `plain:` prefix. Store a supported hashed verifier for such passwords. - Whitespace around `=` is not removed from the username or password. Do not add spaces around the separator. - If a username appears more than once, the last valid entry takes effect. - Malformed entries are skipped. The file must exist and contain at least one valid credential; otherwise, provider initialization fails. - A read error, including invalid UTF-8, stops parsing. Valid credentials read before the error can still be loaded. ### Permission Modes An optional permission mode controls read and write access. The format is: ``` username:permission_mode=password ``` Permission modes are case-insensitive: - `rw`, `readwrite`, or `read_write` - Read and write access (default when omitted) - `ro`, `readonly`, or `read_only` - Read-only access - `wo`, `writeonly`, or `write_only` - Write-only access An unrecognized permission mode makes the entry invalid: file providers skip the entry, while `static_user_provider:cmd` fails to initialize. These modes are not scoped to individual databases or tables. Example configuration with mixed permission modes: ``` admin=admin_pwd alice:readonly=aaa bob:writeonly=bbb viewer:ro=viewer_pwd editor:rw=editor_pwd ``` In this configuration: - `admin` has read-write access (default) - `alice` has read-only access - `bob` has write-only access - `viewer` has read-only access - `editor` has explicitly set read-write access ### Password Formats Since v1.1, passwords can be stored as plaintext or hashed verifiers. The supported formats are: - `plain:` — plaintext. This is the default when no prefix is given. - `pbkdf2_sha256:::` — a PBKDF2-SHA256 hash stored at rest. - `mysql_native_password:` — a hashed verifier for MySQL `mysql_native_password` authentication. - `pg_scram_sha256::::` — a SCRAM-SHA-256 verifier for PostgreSQL SASL authentication. Available since v1.2. The hashed verifier examples below use the password `password` and, where required, the salt `salt`: ``` admin=plain:admin_pwd alice=pbkdf2_sha256:4096:73616c74:c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a bob=mysql_native_password:2470c0c06dee42fd1618bb99005adca2ec9d1e19 carol=pg_scram_sha256:4096:73616c74:945e1c466fc9932efadc23781edc5d1e78d5e10f005933652af1a6105154f084:b9bf0e811b1fb6793671c0cc3adedf7c75cd72291191092ad65878c5a02aad2c ``` Permission modes combine with verifier formats. The verifier goes after the `=`: ``` alice:readonly=pbkdf2_sha256:4096:73616c74:c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a ``` #### Protocol Compatibility Protocol support depends on the verifier format and the authentication method selected by the provider: | Verifier | HTTP/gRPC username/password | PostgreSQL SCRAM-SHA-256 | PostgreSQL cleartext | MySQL `mysql_native_password` | | --- | --- | --- | --- | --- | | `plain:` (or legacy `user=password`) | yes | yes | yes | yes | | `pbkdf2_sha256:...` | yes | no | yes | no | | `mysql_native_password:...` | no | no | no | yes | | `pg_scram_sha256:...` | yes | yes | yes | no | `static_user_provider` and `watch_file_user_provider` negotiate `mysql_native_password`, not `mysql_clear_password`. Users configured with `pbkdf2_sha256` or `pg_scram_sha256` cannot authenticate over MySQL through these providers. Enabling TLS or a client's cleartext authentication plugin does not change the server's selected method. Hashed verifiers protect stored credentials; they do not encrypt network traffic. Enable TLS for production connections, particularly when using HTTP/gRPC username/password authentication or PostgreSQL cleartext authentication. :::warning Breaking change Passwords are prefix-parsed. A legacy plaintext password that literally starts with `plain:`, `pbkdf2_sha256:`, `mysql_native_password:`, or `pg_scram_sha256:` changes meaning. Use the `plain:` prefix to keep the literal value. For example, to keep the literal password `plain:secret`, configure it as `user=plain:plain:secret`. ::: #### PostgreSQL SCRAM-SHA-256 SCRAM-SHA-256 lets PostgreSQL clients authenticate without sending the password in cleartext. GreptimeDB selects SCRAM-SHA-256 only when every configured user has a plaintext password (with or without `plain:`) or a `pg_scram_sha256:` verifier. These two formats can be mixed. With SCRAM-SHA-256, the server also sends an authentication challenge for an unknown username. After the client submits its proof, the server returns the same authentication failure result as for an incorrect password. :::warning A single `pbkdf2_sha256:` or `mysql_native_password:` entry makes PostgreSQL authentication fall back to cleartext for all users of that provider. A user with a `mysql_native_password:` verifier cannot authenticate through that fallback either, because the verifier cannot validate a cleartext password. ::: Channel binding (`SCRAM-SHA-256-PLUS`) is not supported. After configuring all users with SCRAM-compatible credentials, verify the method with libpq or `psql` 16 or newer: ```shell psql "host=127.0.0.1 port=4003 user=carol dbname=public require_auth=scram-sha-256" ``` When the instance has fallen back to cleartext, that command fails with `server requested a cleartext password`. ### Generating Password Verifiers The `greptime user hash-password` command generates password verifiers without starting the server. It is available since v1.1: ```shell ./greptime user hash-password --password-stdin ``` The command reads one line from stdin, removes trailing carriage returns and newlines, and prints the verifier to stdout. Empty input is rejected. `--password-stdin` does not disable terminal echo. In Bash, read the password without echo before piping it to the command: ```bash read -r -s password && printf '%s' "$password" | ./greptime user hash-password --password-stdin ``` Use the output as the password value in the user configuration file: ``` admin=pbkdf2_sha256:4096:: ``` Options: - `--format ` — verifier format, `pbkdf2_sha256` (default), `mysql_native_password`, or `pg_scram_sha256`. - `--password ` — plaintext password. Mutually exclusive with `--password-stdin`; exactly one is required. Prefer `--password-stdin` in scripts, since `--password` can leak through shell history or process listings. - `--password-stdin` — read one line containing the plaintext password from stdin. - `--iterations ` — PBKDF2-SHA256 / SCRAM-SHA-256 iteration count (default `4096`, range `1..=1000000`). - `--salt-len ` — random salt length in bytes (default `16`, range `1..=1024`). - `--salt-hex ` — fixed salt as hex instead of a random one, overriding `--salt-len`. The decoded salt must contain `1..=1024` bytes. `--iterations`, `--salt-len`, and `--salt-hex` apply only to salted formats and are ignored for `mysql_native_password`. To generate a `mysql_native_password` verifier: ```shell ./greptime user hash-password --password-stdin --format mysql_native_password ``` To generate a PostgreSQL SCRAM-SHA-256 verifier: ```shell ./greptime user hash-password --password-stdin --format pg_scram_sha256 ``` ### Starting the Server Set `--user-provider` to `static_user_provider:file:`, replacing `` with the user configuration file path: ```shell ./greptime standalone start --user-provider='static_user_provider:file:' ``` The provider loads valid users and their permission modes into memory at startup. File changes take effect only after a restart. Credentials can also be passed inline with `static_user_provider:cmd`. Separate entries with commas: ```shell ./greptime standalone start --user-provider='static_user_provider:cmd:admin=admin_pwd,alice:ro=alice_pwd' ``` The entries use the same credential syntax as the file. Inline plaintext passwords cannot contain `,` or `=`. Invalid entries fail provider initialization. Command-line credentials can appear in shell history and process listings; use a credential file for deployment. ### Dynamic File Reloading `watch_file_user_provider` monitors a credential file and reloads users and permission modes without restarting the server: ```shell ./greptime standalone start --user-provider='watch_file_user_provider:' ``` The file must exist and contain at least one valid credential at startup. On reload: - If the file cannot be opened or contains no valid credentials, the provider retains the previous configuration. - Otherwise, the loaded credentials replace the previous configuration. Malformed entries are skipped; they do not reject the entire file. Users omitted from the loaded result are removed, including users whose entries became invalid. Reloading does not disconnect existing MySQL or PostgreSQL sessions or update the user information already attached to them. Changed credentials and permission modes apply to subsequent authentication. ## Kubernetes Cluster Configure users in `values.yaml`. See the [Helm Chart Configuration](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#authentication-configuration). --- ## Capacity Plan This guide provides general advice on the CPU, memory, and storage requirements for GreptimeDB. :::note The ratios and sizes on this page come from Greptime's testing and deployments. Use them as a starting point for sizing and validate them against your own workload. ::: GreptimeDB is designed to be lightweight upon startup, which allows for the database to be initiated with minimal server resources. However, when configuring your server capacity for a production environment, there are several key considerations: - Data points processed per second - Query requests per second - Data volume - Data retention policy - Hardware costs To monitor the various metrics of GreptimeDB, please refer to [Monitoring](/user-guide/deployments-administration/monitoring/overview.md). ## CPU Generally, applications that handle many concurrent queries, process large amounts of data, or perform other compute-intensive operations will require more CPU cores. Here are some recommendations for CPU resource usage, but the actual number of CPUs you should use depends on your workload. Consider allocating 30% of your CPU resources for data ingestion, with the remaining 70% to querying and analytics. A recommended guideline for resource allocation is to maintain a CPU to memory ratio of 1:4 (for instance, 8 core to 32 GB). However, if your workload consists primarily of data ingestion with few queries, a ratio of 1:2 (8 core to 16 GB) can also be acceptable. ## Memory In general, the more memory you have, the faster your queries will run. For basic workloads, it's recommended to have at least 8 GB of memory, and 32 GB for more advanced ones. ## Storage GreptimeDB compresses data on disk, and stored data typically comes out at about 1/8 to 1/10 of the raw input size. The actual ratio depends on your schema, column types, and how repetitive the values are. Data can be stored either in a local file system or in cloud storage, such as AWS S3. For more information on storage options, please refer to the [storage configuration](/user-guide/deployments-administration/configuration.md#storage-options) documentation. Cloud storage is highly recommended for data storage due to its simplicity in managing storage. With cloud storage, local disk is used for query-related caches and the Write-Ahead Log (WAL), and about 200GB is usually enough. Size it from your own cache configuration and WAL retention rather than treating 200GB as a fixed requirement. In order to manage the storage costs effectively, it is recommended setting a [retention policy](/user-guide/concepts/features-that-you-concern.md#can-i-set-ttl-or-retention-policy-for-different-tables-or-measurements). ## Example Consider a scenario where your database handles a query rate of about 200 simple queries per second (QPS) and an ingestion rate of approximately 300k data points per second, using cloud storage for data. Given the high ingestion and query rates, here's an example of how you might allocate resources: - CPU: 8 cores - Memory: 32 GB - Storage: 200 GB Whether this allocation holds depends on your schema, average row width, index and TTL settings, and the time range and concurrency of the queries behind the 200 QPS figure. Watch ingestion lag, query latency, and memory when you run your own workload against it. --- ## GreptimeDB Deployment Configuration # Configuration GreptimeDB supports **layered configuration** with the following precedence order (where each item overrides the one below it): - Greptime command line options - Configuration file options - Environment variables - Default values You only need to set up the configurations you require. GreptimeDB will assign default values for any settings not configured. ## How to set up configurations ### Greptime command line options You can specify several configurations using command line arguments. For example, to start GreptimeDB in standalone mode with a configured HTTP address: ```shell greptime standalone start --http-addr 127.0.0.1:4000 ``` For all the options supported by the Greptime command line, refer to the [GreptimeDB Command Line Interface](/reference/command-lines/overview.md). ### Configuration file options You can specify configurations in a TOML file. For example, create a configuration file `standalone.example.toml` as shown below: ```toml [storage] data_home = "./greptimedb_data" type = "File" ``` Then, specify the configuration file using the command line argument `-c [file_path]`. ```sh greptime [standalone | frontend | datanode | flownode | metasrv] start -c config/standalone.example.toml ``` For example, to start in standalone mode: ```bash greptime standalone start -c standalone.example.toml ``` #### Example files Below are example configuration files for each GreptimeDB component, including all available configurations. In actual scenarios, you only need to configure the required options and do not need to configure all options as in the sample file. - [standalone](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/standalone.example.toml) - [frontend](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/frontend.example.toml) - [datanode](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/datanode.example.toml) - [flownode](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/flownode.example.toml) - [metasrv](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/metasrv.example.toml) ### Helm Configurations When deploying GreptimeDB on Kubernetes using Helm charts, you can configure certain settings directly in the Helm `values.yaml` file. Please refer to the [Helm configuration documentation](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) for all Helm-supported configurations. For configurations that are available only in the [options](#options) section of this document, you can [inject complete TOML configuration files](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#injecting-configuration-files) into your deployment. ### Environment variable Every item in the configuration file can be mapped to environment variables. For example, to set the `data_home` configuration item for the datanode using an environment variable: ```toml # ... [storage] data_home = "/data/greptimedb" # ... ``` Use the following shell command to set the environment variable in the following format: ``` export GREPTIMEDB_DATANODE__STORAGE__DATA_HOME=/data/greptimedb ``` #### Environment Variable Rules - Each environment variable should have the component prefix, for example: - `GREPTIMEDB_FRONTEND` - `GREPTIMEDB_METASRV` - `GREPTIMEDB_DATANODE` - `GREPTIMEDB_FLOWNODE` - `GREPTIMEDB_STANDALONE` - Use **double underscore `__`** separators. For example, the data structure `storage.data_home` is transformed to `STORAGE__DATA_HOME`. The environment variable also accepts lists that are separated by commas `,`, for example: ``` GREPTIMEDB_METASRV__META_CLIENT__METASRV_ADDRS=127.0.0.1:3001,127.0.0.1:3002,127.0.0.1:3003 ``` ## Options In this section, we will introduce some main configuration options. For all options, refer to the [Configuration Reference](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/config.md) on Github. ### Runtime options GreptimeDB executes background work on several dedicated Tokio runtimes. These options are valid in all subcommands (`standalone`, `datanode`, `frontend`, and `metasrv`). ```toml [runtime] # The number of threads to execute the runtime for global read operations. # Defaults to the number of CPU cores. global_rt_size = 8 # The number of threads to execute compaction operations. # Defaults to max(num_cpus / 2, 1). compact_rt_size = 4 # The maximum number of blocking threads for compaction operations. # Compaction picker CPU work runs on the compact runtime's blocking thread pool, # so this limit prevents bursts of compaction planning from consuming all available CPUs. # Defaults to max(num_cpus / 2, 1). An explicit 0 is clamped to 1. compact_rt_max_blocking_threads = 4 ``` | Option | Type | Default | Description | | ------------------------------------ | ------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `runtime.global_rt_size` | Integer | The number of CPU cores | The number of threads to execute the runtime for global read operations. | | `runtime.compact_rt_size` | Integer | `max(num_cpus / 2, 1)` | The number of threads to execute compaction operations. | | `runtime.compact_rt_max_blocking_threads` | Integer | `max(num_cpus / 2, 1)` | The maximum number of blocking threads for compaction operations. Compaction picker CPU work runs on this blocking thread pool. An explicit `0` is clamped to `1`. | ### Write memory limiter options Memory limiter options control the total memory used by concurrent write requests across all protocols (HTTP, gRPC, and Arrow Flight). These options are valid in `frontend` and `standalone` subcommands. ```toml # Maximum total memory for all concurrent write request bodies and messages # Set to 0 to disable the limit (unlimited by default) max_in_flight_write_bytes = "1GB" # Policy when write bytes quota is exhausted # Options: "wait" (default, 10s timeout), "wait()" (e.g., "wait(30s)"), "fail" write_bytes_exhausted_policy = "wait" ``` | Option | Type | Default | Description | | ------------------------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `max_in_flight_write_bytes` | String | `0` | Maximum total memory for all concurrent write request bodies and messages (HTTP, gRPC, Flight). Set to `0` to disable the limit (unlimited). Supports units: `B`, `KB`, `MB`, `GB`, etc. Example: `1GB` limits total concurrent writes to 1GB. | | `write_bytes_exhausted_policy` | String | `wait` | Policy when write bytes quota is exhausted. Options: `wait` (default, waits up to 10 seconds), `wait()` (custom timeout, e.g., `wait(30s)`), `fail` (immediately reject the request). | ### Datanode query concurrency limit These options limit how many read queries can run at the same time on a datanode. A query counts toward the limit until it finishes or the client closes the result stream. If the limit is reached, a new query waits up to `concurrent_query_limiter_timeout` for an available slot before it fails. These options are valid in the `datanode` subcommand. ```toml # Maximum number of read queries that can run at the same time on the datanode. # Set to 0 to disable the limit (unlimited by default). max_concurrent_queries = 0 # Maximum time a query waits for an available slot when max_concurrent_queries is reached. concurrent_query_limiter_timeout = "100ms" ``` | Option | Type | Default | Description | | ---------------------------------- | ------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `max_concurrent_queries` | Integer | `0` | Maximum number of read queries that can run at the same time on the datanode. Set to `0` to disable the limit. | | `concurrent_query_limiter_timeout` | String | `100ms` | Maximum time a query waits for an available slot after `max_concurrent_queries` is reached. The query fails if no slot is available in time. | ### Protocol options Protocol options are valid in `frontend` and `standalone` subcommands, specifying protocol server addresses and other protocol-related options. In particular, `http.enable_api_server` lets you start a dedicated public HTTP API server that serves only the `/v1` APIs and `/dashboard`, while keeping the main HTTP server for internal and operational endpoints. This separation is useful when you want to expose GreptimeDB to end users or applications without also exposing admin paths such as health, metrics, config, or debug endpoints. To use it, set `http.enable_api_server = true` and configure `http.api_server_addr` to the address you want to expose externally, while keeping `http.addr` private for operator access. :::tip NOTE The HTTP protocol configuration is available for all GreptimeDB components: `frontend`, `datanode`, `flownode`, and `metasrv`. ::: Below is an example configuration with default values. You can change the values or disable certain protocols in your configuration file. For example, to disable OpenTSDB protocol support, set the `enable` parameter to `false`. Note that HTTP and gRPC protocols cannot be disabled for the database to function correctly. ```toml [http] addr = "127.0.0.1:4000" timeout = "0s" body_limit = "64MB" enable_cors = true # cors_allowed_origins = ["https://example.com"] # Optional: customize allowed origins prom_validation_mode = "strict" experimental_enable_prometheus_native_histogram = false experimental_enable_explain_analyze_stream = true # Enable the dedicated public HTTP API server (serves /v1 and /dashboard only) enable_api_server = false api_server_addr = "127.0.0.1:4006" [grpc] bind_addr = "127.0.0.1:4001" runtime_size = 8 [grpc.tls] mode = "disable" cert_path = "" key_path = "" watch = false [mysql] enable = true addr = "127.0.0.1:4002" runtime_size = 2 keep_alive = "0s" prepared_stmt_cache_size = 10000 [mysql.tls] mode = "disable" cert_path = "" key_path = "" watch = false [postgres] enable = true addr = "127.0.0.1:4003" runtime_size = 2 keep_alive = "0s" [postgres.tls] mode = "disable" cert_path = "" key_path = "" watch = false [opentsdb] enable = true [influxdb] enable = true # Default merge mode for tables automatically created by InfluxDB protocol. # Available values: "last_non_null", "last_row". default_merge_mode = "last_non_null" [jaeger] enable = true [otlp] enable = true trace_ingest_chunk_size = 512 [prom_store] enable = true with_metric_engine = true pending_rows_flush_interval = "0s" max_batch_rows = 100000 max_concurrent_flushes = 256 worker_channel_capacity = 65526 max_inflight_requests = 3000 flow_notification_queue_capacity = 1024 ``` The following table describes the options in detail: | Option | Key | Type | Description | | ---------- | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | http | | | HTTP server options | | | addr | String | Server address, "127.0.0.1:4000" by default | | | timeout | String | HTTP request timeout. Set to `0s` to disable timeout (default: "0s"). When Prometheus Remote Write [batching mode](/user-guide/ingest-data/for-observability/prometheus.md#batching-mode) is enabled, a non-zero timeout less than or equal to `prom_store.pending_rows_flush_interval` plus 1 second is adjusted to that value. | | | body_limit | String | HTTP max body size, "64MB" by default | | | enable_cors | Boolean | Whether to enable HTTP CORS support, true by default. | | | cors_allowed_origins | Array | Customized allowed origins for HTTP CORS. | | | prom_validation_mode | String | Whether to check if strings are valid UTF-8 strings in Prometheus remote write requests. Available options: `strict`(reject any request with invalid UTF-8 strings), `lossy`(replace invalid characters with [UTF-8 REPLACEMENT CHARACTER U+FFFD, which looks like �](https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-23/#G24272)), `unchecked`(do not validate strings). | | | experimental_enable_prometheus_native_histogram | Boolean | Experimental: enable Prometheus remote write v2 native histogram ingestion, false by default. | | | experimental_enable_explain_analyze_stream | Boolean | Experimental: enable `POST /v1/sql/analyze/stream` for streaming `EXPLAIN ANALYZE VERBOSE` metrics, true by default. | | | enable_api_server | Boolean | Whether to start the dedicated public HTTP API server. This server serves only the `/v1` APIs and `/dashboard`, making it safe to expose to end users. The main HTTP server (`addr`) is intended for internal use. Disabled by default; set to `true` to enable. | | | api_server_addr | String | The address to bind the dedicated public HTTP API server, `"127.0.0.1:4006"` by default. Only takes effect when `enable_api_server` is `true`. | | grpc | | | gRPC server options | | | bind_addr | String | The address to bind the gRPC server, "127.0.0.1:4001" by default | | | runtime_size | Integer | The number of server worker threads, 8 by default | | | max_connection_age | String | Maximum lifetime of a gRPC connection that the server keeps it. Refer to ["MAX_CONNECTION_AGE"](https://grpc.io/docs/guides/keepalive/) for details. Defaults to not set. Example: "1h" for 1 hour, "30m" for 30 minutes | | | flight_compression | String | Compression mode for frontend side Arrow IPC service. Available options: `none`: disable all compression, `transport`: only enable gRPC transport compression (zstd), `arrow_ipc`: only enable Arrow IPC compression (lz4), `all`: enable all compression. Default value is `none`. | | mysql | | | MySQL server options | | | enable | Boolean | Whether to enable MySQL protocol, true by default | | | addr | String | Server address, "127.0.0.1:4002" by default | | | runtime_size | Integer | The number of server worker threads, 2 by default | | | keep_alive | String | Server-side keep-alive time. Set to `0s` to disable. | | | prepared_stmt_cache_size | Integer | Maximum entries in the MySQL prepared statement cache, 10000 by default. | | influxdb | | | InfluxDB Protocol options | | | enable | Boolean | Whether to enable InfluxDB protocol in HTTP API, true by default | | | default_merge_mode | String | Default merge mode for tables automatically created by InfluxDB protocol. Available values: `last_non_null`, `last_row`. Default: `last_non_null` | | opentsdb | | | OpenTSDB Protocol options | | | enable | Boolean | Whether to enable OpenTSDB protocol in HTTP API, true by default | | jaeger | | | Jaeger protocol options | | | enable | Boolean | Whether to enable Jaeger protocol in HTTP API, true by default. | | otlp | | | OpenTelemetry protocol options | | | enable | Boolean | Whether to enable OpenTelemetry protocol in HTTP API, true by default. | | | trace_ingest_chunk_size | Integer | Maximum spans per trace ingest chunk. Set to `0` to disable splitting. | | prom_store | | | Prometheus remote storage options | | | enable | Boolean | Whether to enable Prometheus Remote Write and read in HTTP API, true by default | | | with_metric_engine | Boolean | Whether to use the metric engine on Prometheus Remote Write, true by default | | | pending_rows_flush_interval | String | Interval between batch flushes for Prometheus Remote Write. Set to a non-zero duration (e.g. `500ms`) to enable [batching mode](/user-guide/ingest-data/for-observability/prometheus.md#batching-mode). `0s` by default (disabled) | | | max_batch_rows | Integer | Maximum number of rows per batch before a flush is triggered, 100000 by default | | | max_concurrent_flushes | Integer | Maximum number of concurrent flush operations, 256 by default | | | worker_channel_capacity | Integer | Capacity of the internal worker channel for receiving rows, 65526 by default | | | max_inflight_requests | Integer | Maximum number of in-flight write requests waiting for batch completion, 3000 by default | | | flow_notification_queue_capacity | Integer | Maximum number of pending logical-table flow notifications in the shared queue, 1024 by default. Only used in [batching mode](/user-guide/ingest-data/for-observability/prometheus.md#batching-mode). The value must be greater than 0. When the queue is full, notifications are dropped and counted by the `greptime_prom_store_flow_notification_dropped_total` metric. | | postgres | | | PostgresSQL server options | | | enable | Boolean | Whether to enable PostgresSQL protocol, true by default | | | addr | String | Server address, "127.0.0.1:4003" by default | | | runtime_size | Integer | The number of server worker threads, 2 by default | | | keep_alive | String | Server-side keep-alive time. Set to `0s` to disable. | For MySQL, Postgres and gRPC interface, TLS can be configured to enable transport layer security. | Option | Key | Type | Description | | ----------------------------------------- | ----------- | ------- | ------------------------------------------------------------- | | `mysql.tls`, `postgres.tls` or `grpc.tls` | | | TLS configuration for MySQL and Postgres | | | `mode` | String | TLS mode, options are `disable`, `prefer` and `require` | | | `cert_path` | String | File path for TLS certificate | | | `key_path` | String | File path for TLS private key | | | `watch` | Boolean | Watch file system changes and reload certificate and key file. Auto reload is not supported by `grpc.tls`; keep `grpc.tls.watch` set to `false`. | ### Query options The `query` options are valid in standalone, datanode and frontend modes, which controls the query engine's behavior. The following table describes the options in detail: | Option | Key | Type | Description | | ----------- | ------- | ---- | ----------------------------------------------------------------------------------- | | parallelism | Integer | `0` | Parallelism of the query engine. Default to 0, which means the number of CPU cores. | A sample configuration: ```toml [query] parallelism = 0 ``` ### Storage options The `storage` options are valid in datanode and standalone mode, which specify the database data directory and other storage-related options. GreptimeDB supports storing data in local file system, AWS S3 and compatible services (including MinIO, digitalocean space, Tencent Cloud Object Storage(COS), Baidu Object Storage(BOS) and so on), Azure Blob Storage and Aliyun OSS. | Option | Key | Type | Description | | ------- | ------------------------- | ------- | -------------------------------------------------------------------------------- | | storage | | | Storage options | | | type | String | Storage type, supports "File", "S3" and "Oss" etc. | | File | | | Local file storage options, valid when type="File" | | | data_home | String | Database storage root directory, "./greptimedb_data" by default | | | copy_root | String | Root directory for standalone SQL access to local files (default `/copy`). Relative paths in `COPY` and external tables are resolved below this directory; absolute paths are accepted only when they fall inside it. Distributed deployments always reject local-file SQL access. See [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for upgrade guidance. | | S3 | | | AWS S3 storage options, valid when type="S3" | | | name | String | The storage provider name, default is `S3` | | | bucket | String | The S3 bucket name | | | root | String | The root path in S3 bucket | | | endpoint | String | The API endpoint of S3 | | | region | String | The S3 region | | | access_key_id | String | The S3 access key id | | | secret_access_key | String | The S3 secret access key | | | enable_virtual_host_style | Boolean | Send API requests in virtual host style instead of path style. Default is false. | | Oss | | | Aliyun OSS storage options, valid when type="Oss" | | | name | String | The storage provider name, default is `Oss` | | | bucket | String | The OSS bucket name | | | root | String | The root path in OSS bucket | | | endpoint | String | The API endpoint of OSS | | | access_key_id | String | The OSS AccessKey ID | | | access_key_secret | String | The OSS AccessKey Secret | | Azblob | | | Azure Blob Storage options, valid when type="Azblob" | | | name | String | The storage provider name, default is `Azblob` | | | container | String | The container name | | | root | String | The root path in container | | | endpoint | String | The API endpoint of Azure Blob Storage | | | account_name | String | The account name of Azure Blob Storage | | | account_key | String | The access key | | | sas_token | String | The shared access signature | | Gcs | | | Google Cloud Storage options, valid when type="Gcs" | | | name | String | The storage provider name, default is `Gcs` | | | root | String | The root path in the GCS bucket | | | bucket | String | The GCS bucket name | | | scope | String | The GCS service scope | | | credential_path | String | The GCS credentials path | | | endpoint | String | The API endpoint of GCS | A file storage sample configuration: ```toml [storage] data_home = "./greptimedb_data" type = "File" ``` A S3 storage sample configuration: ```toml [storage] type = "S3" bucket = "test_greptimedb" root = "/greptimedb" access_key_id = "" secret_access_key = "" ``` #### Alibaba Cloud OSS authentication with RRSA/OIDC When using ACK RAM Roles for Service Accounts (RRSA) to access OSS, keep the following environment variables injected by RRSA available to the GreptimeDB process: - `ALIBABA_CLOUD_ROLE_ARN` - `ALIBABA_CLOUD_OIDC_PROVIDER_ARN` - `ALIBABA_CLOUD_OIDC_TOKEN_FILE` GreptimeDB v1.1.0 changed the OSS credential lookup order to: configured access keys, environment access keys, ECS RAM Role credentials, then AssumeRoleWithOIDC. If the Pod can obtain ECS node credentials from metadata, those credentials take precedence over RRSA credentials. An OSS permission error does not cause the credential chain to retry with OIDC. For deployments that use RRSA/OIDC, set `ALIBABA_CLOUD_ECS_METADATA_DISABLED=true` in the GreptimeDB process environment to skip ECS metadata credentials. For Kubernetes, add the following to the GreptimeDB container's `env` list (this is a container fragment, not Helm values): ```yaml env: - name: ALIBABA_CLOUD_ECS_METADATA_DISABLED value: "true" ``` Apply this setting to the standalone instance or all datanodes accessing OSS, and restart the affected instances. Keep the RRSA variables and token mount in place. Configured access keys and environment access keys still take precedence; this setting does not force OIDC when those keys are present. Do not disable ECS metadata if the deployment relies on ECS RAM Role credentials. See the [OSS upgrade guidance](/user-guide/deployments-administration/upgrade.md#oss-rrsa-credential-precedence) for upgrade symptoms and verification steps. ### Storage http client `[storage.http_client]` sets the options for the http client that is used to send requests to the storage service. Only applied for storage types "S3", "Oss", "Azblob" and "Gcs". | Key | Type | Default | Description | | ------------------------ | ------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `pool_max_idle_per_host` | Integer | `1024` | The maximum idle connection per host allowed in the pool. | | `connect_timeout` | String | `30s` | The timeout for only the connect phase of a http client. | | `timeout` | String | `30s` | The total request timeout, applied from when the request starts connecting until the response body has finished. Also considered a total deadline. | | `pool_idle_timeout` | String | `90s` | The timeout for idle sockets being kept-alive. | ### Storage engine provider `[[storage.providers]]` setups the table storage engine providers. Based on these providers, you can create a table with a specified storage, see [create table](/reference/sql/create.md#create-table): ```toml # Allows using multiple storages [[storage.providers]] name = "S3" type = "S3" bucket = "test_greptimedb" root = "/greptimedb" access_key_id = "" secret_access_key = "" [[storage.providers]] name = "Gcs" type = "Gcs" bucket = "test_greptimedb" root = "/greptimedb" credential_path = "" ``` All configured providers' names can be used as the `storage` option when creating tables. For storage from the same provider, if you want to use different S3 buckets as storage engines for different tables, you can set different `name` values and specify the `storage` option when creating the table. ### Object storage cache When using remote storage services like AWS S3, Alibaba Cloud OSS, or Azure Blob Storage, fetching data during queries can be time-consuming. To address this, GreptimeDB provides a write cache mechanism to speed up repeated data access. You can configure the cache size and behavior in the mito config if you don't want to use the default values. ```toml [[region_engine]] [region_engine.mito] write_cache_size = "10GiB" # Download files from object storage to fill the cache on write cache miss enable_refill_cache_on_read = true ``` By default, GreptimeDB automatically downloads files from object storage to fill the write cache when there's a cache miss during queries (`enable_refill_cache_on_read = true`). This improves subsequent query performance by keeping frequently accessed data in the write cache. You can disable this behavior by setting `enable_refill_cache_on_read = false` if you want to minimize network traffic or storage costs. Read [Performance Tuning Tips](/user-guide/deployments-administration/performance-tuning/performance-tuning-tips.md) for more detailed info. ### WAL options GreptimeDB supports three WAL storage options—Local WAL, Remote WAL, and Noop WAL. See the [WAL Overview](/user-guide/deployments-administration/wal/overview.md) for a comparison of the options. For detailed configurations, refer to the [Local WAL](/user-guide/deployments-administration/wal/local-wal.md), [Remote WAL](/user-guide/deployments-administration/wal/remote-wal/configuration.md), and [Noop WAL](/user-guide/deployments-administration/wal/noop-wal.md) documentation. ### Logging options `frontend`, `metasrv`, `datanode` and `standalone` can all configure log and tracing related parameters in the `[logging]` section: ```toml [logging] dir = "./greptimedb_data/logs" level = "info" enable_otlp_tracing = false enable_per_region_metrics = false otlp_endpoint = "http://localhost:4318/v1/traces" otlp_export_protocol = "http" append_stdout = true [logging.tracing_sample_ratio] default_ratio = 1.0 ``` - `dir`: log output directory. - `level`: output log level, available log level are `info`, `debug`, `error`, `warn`, the default level is `info`. - `enable_otlp_tracing`: whether to turn on tracing, not turned on by default. - `enable_per_region_metrics`: whether to expose Prometheus per-region query load metrics, including `greptime_mito_region_query_cpu_time` and `greptime_mito_region_query_scanned_bytes`. This option is disabled by default because it creates one time series per region. Query stats reported through heartbeats and exposed in `INFORMATION_SCHEMA.REGION_STATISTICS` are not controlled by this option. - `otlp_endpoint`: The OTLP endpoint for exporting traces. Defaults to `http://localhost:4318/v1/traces` when `otlp_export_protocol` is `http`, and `http://localhost:4317` when it is `grpc`. An endpoint without a scheme is prefixed with `http://`. - `otlp_export_protocol`: The OTLP export protocol for traces. Available values: `http` (HTTP with binary protobuf) and `grpc`. Defaults to `http`. - `otlp_headers`: Additional HTTP headers sent with OTLP export requests, configured as key-value pairs in the `[logging.otlp_headers]` table. Only takes effect when `otlp_export_protocol` is `http`. - `append_stdout`: Whether to append logs to stdout. Defaults to `true`. - `tracing_sample_ratio`: This field can configure the sampling rate of tracing. How to use `tracing_sample_ratio`, please refer to [How to configure tracing sampling rate](/user-guide/deployments-administration/monitoring/tracing.md#guide-how-to-configure-tracing-sampling-rate). How to use distributed tracing, please reference [Tracing](/user-guide/deployments-administration/monitoring/tracing.md#tutorial-use-jaeger-to-trace-greptimedb) ### Pipeline options `frontend` and `standalone` cache pipeline definitions in memory. The cache is configured in the `[pipeline]` section: ```toml [pipeline] cache_ttl = "10s" ``` - `cache_ttl`: Time to live of the local pipeline cache, `10s` by default. In a cluster, a pipeline created or deleted on one Frontend takes effect on the other Frontends after at most this duration. A longer TTL reduces reads of the `greptime_private.pipelines` table and increases that delay. ### Event recording Recorded events are stored in the `greptime_private.events` system table. ```toml [event_recorder] # TTL of the events table. Defaults to 90 days. ttl = "90d" # Omit this option to record all current and future event types. # Use an empty array to disable event recording. event_types = ["create_table", "drop_table"] ``` - `ttl`: The TTL of the events table. The default is `90d`. - `event_types`: The event types to record. When omitted, all current and future event types are recorded. Set it to `[]` to disable event recording. Standalone supports the following event types: ```text create_database, alter_database, drop_database, create_flow, drop_flow, create_table, create_logical_tables, alter_table, alter_logical_tables, drop_table, undrop_table, purge_dropped_table, truncate_table, create_view, drop_view, admin_function ``` `undrop_table` and `purge_dropped_table` require GreptimeDB Enterprise. In distributed deployments, the Frontend supports the following event type: ```text admin_function ``` Metasrv supports the following event types: ```text region_migration, create_database, alter_database, drop_database, create_flow, drop_flow, create_table, create_logical_tables, alter_table, alter_logical_tables, drop_table, undrop_table, purge_dropped_table, truncate_table, create_view, drop_view, repartition, repartition_group, batch_gc, wal_prune ``` `undrop_table` and `purge_dropped_table` require GreptimeDB Enterprise. ### Region engine options The parameters corresponding to different storage engines can be configured for `datanode` and `standalone` in the `[region_engine]` section. Currently, options for `mito` and `metric` region engines are available. Frequently used options: ```toml [[region_engine]] [region_engine.mito] num_workers = 8 manifest_checkpoint_distance = 10 max_background_flushes = 4 max_background_compactions = 2 max_background_purges = 4 auto_flush_interval = "1h" global_write_buffer_size = "1GB" global_write_buffer_reject_size = "2GB" default_region_write_buffer_size = "0" sst_meta_cache_size = "512MB" vector_cache_size = "512MB" page_cache_size = "512MB" write_cache_size = "5GB" write_cache_ttl = "8h" scan_memory_limit = "unlimited" scan_memory_on_exhausted = "fail" min_compaction_interval = "0m" schedule_compaction_after_edit = true default_flat_format = true sst_write_buffer_size = "8MB" max_concurrent_scan_files = 384 [region_engine.mito.index] aux_path = "" staging_size = "2GB" staging_ttl = "7d" metadata_cache_size = "64MiB" content_cache_size = "128MiB" content_cache_page_size = "64KiB" result_cache_size = "128MiB" [region_engine.mito.inverted_index] create_on_flush = "auto" create_on_compaction = "auto" apply_on_query = "auto" mem_threshold_on_create = "auto" intermediate_path = "" ``` Mito selects the memtable implementation for each Region according to its table options and SST format. When `default_flat_format` is `true`, Regions without an explicit `sst_format` use flat SSTs and the bulk memtable. Configure `memtable.type` as a database or table option; `[region_engine.mito.memtable]` is not an engine setting. See [table options](/reference/sql/create.md#table-options). Available options: | Key | Type | Default | Descriptions | | ---------------------------------------- | ------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `num_workers` | Integer | `8` | Number of region workers. | | `manifest_checkpoint_distance` | Integer | `10` | Number of meta action updated to trigger a new checkpoint for the manifest. | | `compress_manifest` | Bool | `false` | Whether to compress manifest and checkpoint file by gzip. | | `max_background_flushes` | Integer | `Auto` | Max number of running background flush jobs (default: 1/2 of cpu cores). | | `max_background_compactions` | Integer | `Auto` | Max number of running background compaction jobs (default: 1/4 of cpu cores). | | `max_background_purges` | Integer | `Auto` | Max number of running background purge jobs (default: cpu cores). | | `auto_flush_interval` | String | `1h` | Interval to auto flush a region if it has not flushed yet. Can be overridden per table with the [`auto_flush_interval` table option](/reference/sql/create.md#table-options). | | `global_write_buffer_size` | String | `1GB` | Global write buffer size for all regions. If not set, it's default to 1/8 of OS memory with a max limitation of 1GB. | | `global_write_buffer_reject_size` | String | `2GB` | Global write buffer size threshold to reject write requests. If not set, it's default to 2 times of `global_write_buffer_size` | | `default_region_write_buffer_size` | String | `0` | Default per-region write buffer stall threshold. For a positive value, GreptimeDB schedules a flush when mutable memtable usage reaches half the value, stalls writes at the value, and rejects writes at twice the value. Setting it to `0` disables the default per-region limit. A table's `write_buffer_size` overrides this value, including an explicit `0` that disables the limit for that table. | | `sst_meta_cache_size` | String | Auto | Cache size for SST metadata. Setting it to 0 to disable the cache.If not set, it's default to 1/8 of OS memory with a max limitation of 512MB. | | `vector_cache_size` | String | `512MB` | Cache size for vectors and arrow arrays. Setting it to 0 to disable the cache.If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. | | `page_cache_size` | String | `512MB` | Cache size for pages of SST row groups. Setting it to 0 to disable the cache.If not set, it's default to 1/8 of OS memory. | | `write_cache_size` | String | `5GiB` | Capacity for write cache. If your disk space is sufficient, it is recommended to set it larger. | | `write_cache_ttl` | String | `8h` | TTL for write cache. Defaults to 8 hours. | | `preload_index_cache` | Bool | `true` | Preload index (puffin) files into cache on region open (default: true).When enabled, index files are loaded into the write cache during region initialization,which can improve query performance at the cost of longer startup times. | | `index_cache_percent` | Integer | `20` | Percentage of write cache capacity allocated for index (puffin) files (default: 20).The remaining capacity is used for data (parquet) files.Must be between 0 and 100 (exclusive). For example, with a 5GiB write cache and 20% allocation,1GiB is reserved for index files and 4GiB for data files. | | `enable_refill_cache_on_read` | Bool | `true` | Enable refilling cache on read operations (default: true).When disabled, cache refilling on read won't happen. | | `manifest_cache_size` | String | `256MB` | Capacity for manifest cache (default: 256MB). | | `selector_result_cache_size` | String | `512MB` | Cache size for time series selector (e.g. `last_value()`). Setting it to 0 to disable the cache.If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. | | `range_result_cache_size` | String | `512MB` | Cache size for flat range scan results. Setting it to 0 to disable the cache.If not set, it's default to 1/16 of OS memory with a max limitation of 512MB. | | `prefilter_result_cache_size` | String | `128MB` | Cache size for prefilter results. Setting it to 0 to disable the cache.If not set, it's default to 1/32 of OS memory with a max limitation of 128MB. | | `sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. | | `max_concurrent_scan_files` | Integer | `384` | Maximum number of SST files to scan concurrently. | | `allow_stale_entries` | Bool | `false` | Whether to allow stale WAL entries during replay. | | `scan_memory_limit` | String | `unlimited` | Memory limit for table scans across all queries. Supports absolute size (e.g., "2GB") or percentage of system memory (e.g., "20%"). Setting it to 0 or "unlimited" disables the limit. | | `scan_memory_on_exhausted` | String | `fail` | Behavior when scan memory is exhausted. Options: `fail` (fail fast), `wait` or `wait()` (wait for memory). | | `min_compaction_interval` | String | `0m` | Minimum time interval between two compactions. Set to "0m" (default) to allow compactions to run immediately without restriction. | | `schedule_compaction_after_edit` | Bool | `true` | Whether to allow scheduling a compaction after a successful region edit.Setting this to `true` is a necessary but not sufficient condition for scheduling compaction after a region edit. Other constraints, such as `min_compaction_interval`, may still prevent compaction from being scheduled.Setting this to `false` guarantees that compaction will not be scheduled after a region edit. | | `default_flat_format` | Bool | `true` | Whether Regions without an explicit `sst_format` use flat SSTs. Flat SSTs use the bulk memtable. | | `scan_parallelism` | Integer | `0` | (Deprecated, use `max_concurrent_scan_files` instead) Legacy option for scan parallelism. | | `index` | -- | -- | The options for index in Mito engine. | | `index.aux_path` | String | `""` | Auxiliary directory path for the index in the filesystem. This path is used to store intermediate files for creating the index and staging files for searching the index. It defaults to `{data_home}/index_intermediate`. The default name for this directory is `index_intermediate` for backward compatibility. This path contains two subdirectories: `__intm` for storing intermediate files used during index creation, and `staging` for storing staging files used during index searching. | | `index.staging_size` | String | `2GB` | The maximum capacity of the staging directory. | | `index.staging_ttl` | String | `7d` | TTL for staging directory. Defaults to 7 days. Setting to "0s" disables TTL. | | `index.metadata_cache_size` | String | `64MiB` | Cache size for index metadata.If not set, it's default to 1/32 of OS memory with a max limitation of 64MiB. | | `index.content_cache_size` | String | `128MiB` | Cache size for index content.If not set, it's default to 1/16 of OS memory with a max limitation of 128MiB. | | `index.content_cache_page_size` | String | `64KiB` | Page size for index content cache. | | `index.result_cache_size` | String | `128MiB` | Cache size for index query results.If not set, it's default to 1/16 of OS memory with a max limitation of 128MiB. | | `inverted_index` | -- | -- | The options for inverted index in Mito engine. | | `inverted_index.create_on_flush` | String | `auto` | Whether to create the index on flush.- `auto`: automatically- `disable`: never | | `inverted_index.create_on_compaction` | String | `auto` | Whether to create the index on compaction.- `auto`: automatically- `disable`: never | | `inverted_index.apply_on_query` | String | `auto` | Whether to apply the index on query- `auto`: automatically- `disable`: never | | `inverted_index.mem_threshold_on_create` | String | `auto` | Memory threshold for performing an external sort during index creation.- `auto`: 1/16 of the system memory- `unlimited`: disable external sorting, forcing all sorting operations to happen in memory- A fixed size, such as `64MB` | | `inverted_index.intermediate_path` | String | `""` | File system path to store intermediate files for external sorting (default `{data_home}/index_intermediate`). | The `metric` engine is optimized for handling metrics data with a large number of small tables. :::note Starting from v1.2, sparse primary key encoding is always enabled for the metric engine and cannot be disabled. It encodes only non-null primary key columns, improving write and query performance. Any `sparse_primary_key_encoding` setting in your configuration file is accepted but has no effect. ::: ### Specify meta client The `meta_client` options are valid in `datanode` and `frontend` mode, which specify the Metasrv client information. ```toml metasrv_addrs = ["127.0.0.1:3002"] timeout = "3s" connect_timeout = "1s" ddl_timeout = "10s" tcp_nodelay = true ``` The `meta_client` configures the Metasrv client, including: - `metasrv_addrs`: The Metasrv address list. - `timeout`: operation timeout, `3s` by default. - `connect_timeout`, connect server timeout, `1s` by default. - `ddl_timeout`, DDL execution timeout, `10s` by default. - `tcp_nodelay`, `TCP_NODELAY` option for accepted connections, true by default. ### Heartbeat configuration In distributed mode, heartbeat intervals are controlled by Metasrv using the `heartbeat_interval` option. ```toml # Metasrv-only option heartbeat_interval = "3s" # Top-level option for reporting selected env vars in heartbeat extensions heartbeat_env_vars = ["AZ", "REGION"] ``` | Key | Type | Default | Description | | ----------------------- | ------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `heartbeat_interval` | String | `3s` | Metasrv base heartbeat interval. The frontend heartbeat interval is 6 times this value, and the datanode/flownode heartbeat interval is equal to this value. Heartbeat intervals are negotiated from Metasrv during handshake; local node configs do not override this. | | `heartbeat_env_vars` | Array | `[]` | A top-level option. Lists the environment variable keys to read at startup and report to Metasrv through heartbeat messages. The values are sent as heartbeat extensions and stored in Metasrv's node information. For example, `heartbeat_env_vars = ["AZ", "REGION"]` collects the `AZ` and `REGION` environment variables. **Do not include sensitive variables** (e.g., secrets or credentials), as their values will be transmitted to and stored in Metasrv. | ### Default time zone configuration The `default_timezone` option is applicable in both `frontend` and `standalone` modes, with a default value of `UTC`. It specifies the default client timezone for interactions with GreptimeDB. If the time zone is [specified in the clients](/user-guide/timezone.md#specify-time-zone-in-clients), this option will be overridden for that client session. ```toml default_timezone = "UTC" ``` The `default_timezone` value can be any named time zone, such as `Europe/Berlin` or `Asia/Shanghai`. For information on how the client time zone affects data ingestion and querying, refer to the [Time Zone](/user-guide/timezone.md#impact-of-time-zone-on-sql-statements) guide. ### Metasrv-only configuration The `datanode.client` options configure outbound gRPC connections from Metasrv to Datanodes. The client message size limits are independent of the `[grpc]` server message size limits. ```toml # The working home directory. data_home = "./greptimedb_data" # Store server address default to etcd store. # For postgres store, the format is: # "password=password dbname=postgres user=postgres host=localhost port=5432" # For mysql store, the format is: # "mysql://user:password@ip:port/dbname" # For etcd store, the format is: # "127.0.0.1:2379" store_addrs = ["127.0.0.1:2379"] # If it's not empty, the metasrv will store all data with this key prefix. store_key_prefix = "" # The datastore for meta server. # Available values: # - `etcd_store` (default value) # - `memory_store` # - `postgres_store` # - `mysql_store` backend = "etcd_store" # Table name in RDS to store metadata. Effect when using a RDS kvbackend. # **Only used when backend is RDS kvbackend.** meta_table_name = "greptime_metakv" # Optional PostgreSQL schema for metadata table and election table name qualification. # When PostgreSQL public schema is not writable (e.g., PostgreSQL 15+ with restricted public), # set this to a writable schema. GreptimeDB will use `meta_schema_name`.`meta_table_name`. # **Only used when backend is `postgres_store`.** meta_schema_name = "greptime_schema" # Automatically create PostgreSQL schema if it doesn't exist. # **Only used when backend is `postgres_store`.** auto_create_schema = true # Advisory lock id in PostgreSQL for election. Effect when using PostgreSQL as kvbackend # Only used when backend is `postgres_store`. meta_election_lock_id = 1 # Datanode selector type. # - `round_robin` (default value) # - `lease_based` # - `load_based` # For details, please see "https://docs.greptime.com/contributor-guide/metasrv/selector". selector = "round_robin" # Whether to enable region failover. # This feature is only available on GreptimeDB running on cluster mode and: # - Using Remote WAL, or Local WAL with `allow_region_failover_on_local_wal` set to `true` # - Using shared storage (e.g., S3) enable_region_failover = false ## The delay before starting region failure detection. ## This delay helps prevent Metasrv from triggering unnecessary region failovers before all Datanodes are fully started. ## Especially useful when the cluster is not deployed with GreptimeDB Operator and maintenance mode is not enabled. region_failure_detector_initialization_delay = "10m" # Whether to allow region failover on local WAL. # **This option is not recommended to be set to true, # because it may lead to data loss during failover.** allow_region_failover_on_local_wal = false ## Max allowed idle time before removing node info from metasrv memory. node_max_idle_time = "24hours" # Base heartbeat interval for calculating distributed time constants. # Heartbeat intervals are negotiated from metasrv during handshake; local node configs do not override this. # heartbeat_interval = "3s" # Whether to enable GreptimeDB telemetry. Enabled by default. # enable_telemetry = true # TLS configuration for kv store backend (applicable for etcd, PostgreSQL, and MySQL backends). # If TLS is configured here and in `store_addrs`, this section overrides `store_addrs` TLS settings. [backend_tls] mode = "prefer" cert_path = "" key_path = "" ca_cert_path = "" ## The backend client options. ## Currently, only applicable when using etcd as the metadata store. [backend_client] ## The keep alive timeout for backend client. keep_alive_timeout = "3s" ## The keep alive interval for backend client. keep_alive_interval = "10s" ## The connect timeout for backend client. connect_timeout = "3s" ## The gRPC server options. [grpc] bind_addr = "127.0.0.1:3002" server_addr = "127.0.0.1:3002" runtime_size = 8 ## The maximum receive message size for gRPC server. max_recv_message_size = "512MB" ## The maximum send message size for gRPC server. max_send_message_size = "512MB" ## The server side HTTP/2 keep-alive interval http2_keep_alive_interval = "10s" ## The server side HTTP/2 keep-alive timeout. http2_keep_alive_timeout = "3s" ## The HTTP server options. [http] addr = "127.0.0.1:4000" timeout = "0s" body_limit = "64MB" ## Enable the dedicated public HTTP API server (serves /v1 and /dashboard only). enable_api_server = false api_server_addr = "127.0.0.1:4006" ## Procedure storage options. [procedure] ## Procedure max retry time. max_retry_times = 12 ## Initial retry delay of procedures, increases exponentially retry_delay = "500ms" ## Auto split large values. max_metadata_value_size = "1500KiB" ## Max running procedures. ## The maximum number of procedures that can be running at the same time. ## If the number of running procedures exceeds this limit, the procedure will be rejected. max_running_procedures = 128 # Failure detectors options. # GreptimeDB uses the Phi Accrual Failure Detector algorithm to detect datanode failures. [failure_detector] ## Maximum acceptable φ before the peer is treated as failed. ## Lower values react faster but yield more false positives. threshold = 8.0 ## The minimum standard deviation of the heartbeat intervals. ## So tiny variations don't make φ explode. Prevents hypersensitivity when heartbeat intervals barely vary. min_std_deviation = "100ms" ## The acceptable pause duration between heartbeats. ## Additional extra grace period to the learned mean interval before φ rises, absorbing temporary network hiccups or GC pauses. acceptable_heartbeat_pause = "10000ms" ## Datanode options. [datanode] ## Datanode client options. [datanode.client] ## Operation timeout. timeout = "10s" ## Connect server timeout. connect_timeout = "10s" ## `TCP_NODELAY` option for accepted connections. tcp_nodelay = true ## The maximum receive message size for the gRPC client. max_recv_message_size = "512MB" ## The maximum send message size for the gRPC client. max_send_message_size = "512MB" [wal] # Available wal providers: # - `raft_engine` (default): there're none raft-engine wal config since metasrv only involves in remote wal currently. # - `kafka`: metasrv **have to be** configured with kafka wal config when using kafka wal provider in datanode. provider = "raft_engine" # Kafka wal config. ## The broker endpoints of the Kafka cluster. broker_endpoints = ["127.0.0.1:9092"] ## Automatically create topics for WAL. ## Set to `true` to automatically create topics for WAL. ## Otherwise, use topics named `topic_name_prefix_[0..num_topics)` auto_create_topics = true ## Number of topics. num_topics = 64 ## Topic selector type. ## Available selector types: ## - `round_robin` (default) selector_type = "round_robin" ## A Kafka topic is constructed by concatenating `topic_name_prefix` and `topic_id`. topic_name_prefix = "greptimedb_wal_topic" ## Expected number of replicas of each partition. replication_factor = 1 ## Above which a topic creation operation will be cancelled. create_topic_timeout = "30s" ## The connect timeout for kafka client. ## **It's only used when the provider is `kafka`**. connect_timeout = "3s" ## The timeout for kafka client. ## **It's only used when the provider is `kafka`**. timeout = "3s" # The Kafka SASL configuration. # **It's only used when the provider is `kafka`**. # Available SASL mechanisms: # - `PLAIN` # - `SCRAM-SHA-256` # - `SCRAM-SHA-512` # [wal.sasl] # type = "SCRAM-SHA-512" # username = "user_kafka" # password = "secret" # The Kafka TLS configuration. # **It's only used when the provider is `kafka`**. # [wal.tls] # server_ca_cert_path = "/path/to/server_cert" # client_cert_path = "/path/to/client_cert" # client_key_path = "/path/to/key" ``` | Key | Type | Default | Descriptions | | --------------------------------------------- | ------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data_home` | String | `./greptimedb_data/metasrv/` | The working home directory. | | `bind_addr` | String | `127.0.0.1:3002` | The bind address of metasrv. | | `server_addr` | String | `127.0.0.1:3002` | The communication server address for frontend and datanode to connect to metasrv, "127.0.0.1:3002" by default for localhost. | | `store_addrs` | Array | `["127.0.0.1:2379"]` | Store server address. Configure the address based on your backend type, for example:- Use `127.0.0.1:2379` to connect to etcd- Use `"password=password dbname=postgres user=postgres host=localhost port=5432"` to connect to postgres- Use `"mysql://user:password@ip:port/dbname"` to connect to mysql | | `selector` | String | `round_robin` | Datanode selector type.- `round_robin` (default value)- `lease_based`- `load_based`For details, see [Selector](/contributor-guide/metasrv/selector.md) | | `enable_region_failover` | Bool | `false` | Whether to enable region failover.This feature is only available on GreptimeDB running on cluster mode and:- Using Remote WAL, or Local WAL with `allow_region_failover_on_local_wal` set to `true`- Using shared storage (e.g., S3). | | `region_failure_detector_initialization_delay` | String | `10m` | The delay before starting region failure detection. This delay helps prevent Metasrv from triggering unnecessary region failovers before all Datanodes are fully started. Especially useful when the cluster is not deployed with GreptimeDB Operator and maintenance mode is not enabled. | | `allow_region_failover_on_local_wal` | Bool | `false` | Whether to allow region failover on local WAL.**This option is not recommended to be set to true, because it may lead to data loss during failover.** | | `node_max_idle_time` | String | `24hours` | Max allowed idle time before removing node info from metasrv memory. Nodes that haven't sent heartbeats for this duration will be considered inactive and removed. | | `backend_tls` | -- | -- | TLS configuration for kv store backend. Applicable for etcd, PostgreSQL, and MySQL backends. If TLS is configured here and in `store_addrs`, this section overrides `store_addrs` TLS settings. | | `backend_tls.mode` | String | `prefer` | TLS mode for the kv store backend. Options are `disable`, `prefer`, `require`, `verify_ca`, and `verify_full`. | | `backend_tls.cert_path` | String | -- | File path for the client TLS certificate. | | `backend_tls.key_path` | String | -- | File path for the client TLS private key. | | `backend_tls.ca_cert_path` | String | -- | File path for the trusted CA certificate. | | `backend_client` | -- | -- | The backend client options.Currently, only applicable when using etcd as the metadata store. | | `backend_client.keep_alive_timeout` | String | `3s` | The keep alive timeout for backend client. | | `backend_client.keep_alive_interval` | String | `10s` | The keep alive interval for backend client. | | `backend_client.connect_timeout` | String | `3s` | The connect timeout for backend client. | | `grpc` | -- | -- | The gRPC server options. | | `grpc.bind_addr` | String | `127.0.0.1:3002` | The address to bind the gRPC server. | | `grpc.server_addr` | String | `127.0.0.1:3002` | The communication server address for frontend and datanode to connect to metasrv. | | `grpc.runtime_size` | Integer | `8` | The number of server worker threads. | | `grpc.max_recv_message_size` | String | `512MB` | The maximum receive message size for the gRPC server. | | `grpc.max_send_message_size` | String | `512MB` | The maximum send message size for the gRPC server. | | `grpc.http2_keep_alive_interval` | String | `10s` | The server side HTTP/2 keep-alive interval. | | `grpc.http2_keep_alive_timeout` | String | `3s` | The server side HTTP/2 keep-alive timeout. | | `http` | -- | -- | HTTP server options. | | `http.addr` | String | `127.0.0.1:4000` | HTTP server address. | | `http.timeout` | String | `0s` | HTTP request timeout. Set to `0s` to disable timeout. | | `http.body_limit` | String | `64MB` | HTTP max body size. | | `http.enable_api_server` | Bool | `false` | Whether to start the dedicated public HTTP API server. When enabled, a separate server is started at `api_server_addr` that serves only `/v1` APIs and `/dashboard`. The main HTTP server is kept for internal use. | | `http.api_server_addr` | String | `127.0.0.1:4006` | The address to bind the dedicated public HTTP API server. Only takes effect when `enable_api_server` is `true`. | | `backend` | String | `etcd_store` | The datastore for metasrv.- `etcd_store` (default)- `memory_store` (In memory metadata storage - only used for testing.)- `postgres_store`- `mysql_store` | | `meta_table_name` | String | `greptime_metakv` | Table name in RDS to store metadata. Effect when using a RDS kvbackend.**Only used when backend is RDS kvbackend.** | | `meta_schema_name` | String | -- | Optional PostgreSQL schema for metadata table and election table name qualification. When PostgreSQL public schema is not writable (e.g., PostgreSQL 15+ with restricted public), set this to a writable schema. GreptimeDB will use `meta_schema_name.meta_table_name`.**Only used when backend is `postgres_store`.** | | `auto_create_schema` | Bool | `true` | Automatically create PostgreSQL schema if it doesn't exist. When enabled, the system will execute `CREATE SCHEMA IF NOT EXISTS ` before creating metadata tables. This is useful in production environments where manual schema creation may be restricted. Note: The PostgreSQL user must have CREATE SCHEMA permission for this to work.**Only used when backend is `postgres_store`.** | | `meta_election_lock_id` | Integer | `1` | Advisory lock id in PostgreSQL for election. Effect when using PostgreSQL as kvbackend**Only used when backend is `postgres_store`.** | | `procedure` | -- | -- | Procedure storage options. | | `procedure.max_retry_times` | Integer | `12` | Procedure max retry time. | | `procedure.retry_delay` | String | `500ms` | Initial retry delay of procedures, increases exponentially. | | `procedure.max_metadata_value_size` | String | `1500KiB` | Maximum size of a procedure metadata value before it is split into multiple backend entries. | | `procedure.max_running_procedures` | Integer | `128` | The maximum number of procedures that can be running at the same time. If the number of running procedures exceeds this limit, the procedure will be rejected. | | `failure_detector` | -- | -- | -- | | `failure_detector.threshold` | Float | `8.0` | Maximum acceptable φ before the peer is treated as failed.Lower values react faster but yield more false positives. | | `failure_detector.min_std_deviation` | String | `100ms` | The minimum standard deviation of the heartbeat intervals.So tiny variations don't make φ explode. Prevents hypersensitivity when heartbeat intervals barely vary. | | `failure_detector.acceptable_heartbeat_pause` | String | `10000ms` | The acceptable pause duration between heartbeats.Additional extra grace period to the learned mean interval before φ rises, absorbing temporary network hiccups or GC pauses. | | `datanode` | -- | -- | Datanode options. | | `datanode.client` | -- | -- | Datanode client options. | | `datanode.client.timeout` | String | `10s` | Operation timeout. | | `datanode.client.connect_timeout` | String | `10s` | Connect server timeout. | | `datanode.client.tcp_nodelay` | Bool | `true` | `TCP_NODELAY` option for accepted connections. | | `datanode.client.max_recv_message_size` | String | `512MB` | The maximum receive message size for the gRPC client. | | `datanode.client.max_send_message_size` | String | `512MB` | The maximum send message size for the gRPC client. | | `wal` | -- | -- | -- | | `wal.provider` | String | `raft_engine` | -- | | `wal.broker_endpoints` | Array | -- | The broker endpoints of the Kafka cluster. | | `wal.auto_prune_interval` | String | `0s` | Interval of automatically WAL pruning.Set to `0s` to disable automatically WAL pruning which delete unused remote WAL entries periodically. | | `wal.auto_prune_logical_delete` | Bool | `false` | Whether automatic WAL pruning only updates metadata and skips Kafka `DeleteRecords`. See [Remote WAL configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md#metasrv-configuration). | | `wal.flush_trigger_size` | String | `512MB` | Estimated WAL size of a region above which Metasrv triggers a flush of the region. See [Remote WAL configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md#metasrv-configuration). | | `wal.checkpoint_trigger_size` | String | `128MB` | Estimated WAL size of a region above which Metasrv triggers a checkpoint of the region. See [Remote WAL configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md#metasrv-configuration). | | `wal.auto_prune_parallelism` | Integer | `10` | Concurrent task limit for automatically WAL pruning. Each task is responsible for WAL pruning for a kafka topic. | | `wal.num_topics` | Integer | `64` | Number of topics. | | `wal.selector_type` | String | `round_robin` | Topic selector type.Available selector types:- `round_robin` (default) | | `wal.topic_name_prefix` | String | `greptimedb_wal_topic` | A Kafka topic is constructed by concatenating `topic_name_prefix` and `topic_id`. | | `wal.replication_factor` | Integer | `1` | Expected number of replicas of each partition. | | `wal.create_topic_timeout` | String | `30s` | Above which a topic creation operation will be cancelled. | | `wal.connect_timeout` | String | `3s` | The connect timeout for kafka client.**It's only used when the provider is `kafka`**. | | `wal.timeout` | String | `3s` | The timeout for kafka client.**It's only used when the provider is `kafka`**. | | `wal.sasl` | String | -- | The Kafka SASL configuration. | | `wal.sasl.type` | String | -- | The SASL mechanisms, available values: `PLAIN`, `SCRAM-SHA-256`, `SCRAM-SHA-512`. | | `wal.sasl.username` | String | -- | The SASL username. | | `wal.sasl.password` | String | -- | The SASL password. | | `wal.tls` | String | -- | The Kafka TLS configuration. | | `wal.tls.server_ca_cert_path` | String | -- | The path of trusted server ca certs. | | `wal.tls.client_cert_path` | String | -- | The path of client cert (Used for enable mTLS). | | `wal.tls.client_key_path` | String | -- | The path of client key (Used for enable mTLS). | ### Datanode-only configuration ```toml node_id = 42 [grpc] bind_addr = "127.0.0.1:3001" server_addr = "127.0.0.1:3001" runtime_size = 8 [runtime] query_rt_size = 7 ingest_rt_size = 8 ``` | Key | Type | Description | | ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | node_id | Integer | The datanode identifier, should be unique. | | grpc.bind_addr | String | The address to bind the gRPC server, `127.0.0.1:3001` by default. | | grpc.server_addr | String | The address advertised to the metasrv, and used for connections from outside the host. If left empty or unset, the server will automatically use the IP address of the first network interface on the host, with the same port number as the one specified in `grpc.bind_addr`. | | grpc.runtime_size | Integer | The number of gRPC server worker threads, 8 by default. | | runtime.query_rt_size | Integer | The number of threads to execute datanode query operations. Defaults to `max(num_cpus - 1, 1)`. | | runtime.ingest_rt_size | Integer | The number of threads to execute datanode ingestion operations. Defaults to the number of CPU cores. | For the common runtime options shared by all components, see [Runtime options](#runtime-options). ### Frontend-only configuration The `datanode.client` options configure outbound gRPC connections from Frontend to Datanodes. The client message size limits are independent of the `[grpc]` server message size limits. ```toml [datanode] [datanode.client] connect_timeout = "1s" tcp_nodelay = true max_recv_message_size = "512MB" max_send_message_size = "512MB" ``` | Key | Type | Default | Description | | ----------------------------------------------- | ------ | ------- | ------------------------------------------------------ | | `datanode` | -- | -- | Datanode options. | | `datanode.client` | -- | -- | Datanode client options. | | `datanode.client.connect_timeout` | String | `1s` | Connect server timeout. | | `datanode.client.tcp_nodelay` | Bool | `true` | `TCP_NODELAY` option for accepted connections. | | `datanode.client.max_recv_message_size` | String | `512MB` | The maximum receive message size for the gRPC client. | | `datanode.client.max_send_message_size` | String | `512MB` | The maximum send message size for the gRPC client. | --- ## Common Helm Chart Configurations For each Helm Chart, you can create a `values.yaml` file for configuration. When you need to apply configurations, you can use the `helm upgrade` command: ``` helm upgrade --install ${release-name} ${chart-name} --namespace ${namespace} -f values.yaml ``` ## GreptimeDB Cluster Chart For complete configuration options, please refer to [GreptimeDB Cluster Chart](https://github.com/GreptimeTeam/helm-charts/tree/main/charts/greptimedb-cluster/README.md). ### GreptimeDB Image Configuration The top-level variable `image` is used to configure the global GreptimeDB image for the cluster, as shown below: ```yaml image: # -- The image registry registry: docker.io # -- The image repository repository: greptime/greptimedb # -- The image tag tag: "v1.2.1" # -- The image pull secrets pullSecrets: [] ``` If you want to configure different images for each role in the cluster, you can use the `${role}.podTemplate.main.image` field (where `role` can be `meta`, `frontend`, `datanode` and `flownode`). This field will **override** the top-level `image` configuration, as shown below: ```yaml image: # -- The image registry registry: docker.io # -- The image repository repository: greptime/greptimedb # -- The image tag tag: "v1.2.1" # -- The image pull secrets pullSecrets: [] frontend: podTemplate: main: image: "greptime/greptimedb:latest" ``` In this case, the `frontend` image will be set to `greptime/greptimedb:latest`, while other components will use the top-level `image` configuration. ### Service Ports Configuration You can configure service ports using the following fields: - `httpServicePort`: Configures the HTTP service port, default `4000` - `grpcServicePort`: Configures the gRPC service port, default `4001` - `mysqlServicePort`: Configures the MySQL service port, default `4002` - `postgresServicePort`: Configures the PostgreSQL service port, default `4003` - `frontend.internalPort`: Configures the internal service port, default `4010`. This port is used for internal communication between GreptimeDB components. ### Datanode Storage Configuration You can configure Datanode storage through the `datanode.storage` field, as shown below: ```yaml datanode: storage: # -- Storage class for datanode persistent volume storageClassName: null # -- Storage size for datanode persistent volume storageSize: 20Gi # -- Storage retain policy for datanode persistent volume storageRetainPolicy: Retain # -- The dataHome directory, default is "/data/greptimedb/" dataHome: "/data/greptimedb" ``` - `storageClassName`: Configures the StorageClass, defaults to Kubernetes current default StorageClass - `storageSize`: Configures the storage size, default `20Gi`. You can use common capacity units, such as `50Gi`, etc. - `storageRetainPolicy`: Configures the storage retention policy, default `Retain`. If set to `Delete`, the storage will be deleted when the cluster is deleted - `dataHome`: Configures the data directory, default `/data/greptimedb/` ### Resource Configuration The top-level variable `base.podTemplate.main.resources` is used to globally configure resources for each role, as shown below: ```yaml base: podTemplate: main: resources: requests: memory: "1Gi" cpu: "1" limits: memory: "2Gi" cpu: "2" ``` If you want to configure different resources for each role in the cluster, you can use the `${role}.podTemplate.main.resources` field (where `role` can be `meta`, `frontend`, `datanode`, `flownode`, etc.). This field will **override** the top-level `base.podTemplate.main.resources` configuration, as shown below: ```yaml base: podTemplate: main: resources: requests: memory: "1Gi" cpu: "1" limits: memory: "2Gi" cpu: "2" frontend: podTemplate: main: resources: requests: cpu: "2" memory: "4Gi" limits: cpu: "4" memory: "8Gi" ``` ### Role Replicas Configuration For different roles, the number of replicas can be configured through the `${role}.replicas` field, as shown below: ```yaml frontend: replicas: 3 datanode: replicas: 3 ``` You can achieve horizontal scaling by configuring the number of replicas. ### Environment Variable Configuration You can configure global environment variables through the `base.podTemplate.main.env` field, and configure different environment variables for each Role through the `${role}.podTemplate.main.env` field, as shown below: ```yaml base: podTemplate: main: env: - name: GLOBAL_ENV value: "global_value" frontend: podTemplate: main: env: - name: FRONTEND_ENV value: "frontend_value" ``` ### Injecting Configuration Files For different Role services, youcan inject custom TOML configuration files through the `${role}.configData` field, as shown below: ```yaml datanode: configData: | [[region_engine]] [region_engine.mito] # Number of region workers num_workers = 8 ``` You can learn about GreptimeDB configuration options through [config.md](https://github.com/GreptimeTeam/greptimedb/blob/main/config/config.md). In addition to using the `${role}.configData` field to inject configuration files, you can also specify corresponding files through `${role}.configFile`, as shown below: ```yaml frontend: configFile: "configs/frontend.toml" ``` In this case, ensure that the configuration file path matches the directory where the `helm upgrade` command is executed. :::note User-injected configuration files have lower priority by default than configuration items managed by GreptimeDB Operator. Some configuration items can only be configured through GreptimeDB Operator, and these items are exposed by default in `values.yaml`. The following default configurations are managed by GreptimeDB Operator: - Logging configuration; - Datanode's Node ID; ::: ### Authentication Configuration The Helm Chart does not enable User Provider mode authentication by default. You can enable User Provider mode authentication and configure user information through the `auth.enabled` field, as shown below: ```yaml auth: enabled: true users: - username: "admin" password: "admin" permission: "readwrite" - username: "grafana" password: "grafana_pwd" permission: "readonly" - username: "telegraf" password: "telegraf_pwd" permission: "writeonly" ``` GreptimeDB supports three permission modes: | Mode | Shorthand | Allowed Operations | Typical Use Cases | |------|-----------|-------------------|-------------------| | `readwrite` | `rw` | Read + Write | Administrators, development environments | | `readonly` | `ro` | Read only | Dashboards, reporting systems, read replicas | | `writeonly` | `wo` | Write only | Data collectors, IoT devices, log aggregators | :::note Users without an explicit permission mode default to `readwrite`, maintaining backward compatibility. ### Logging Configuration The top-level variable `logging` is used to configure global logging levels, as shown below: ```yaml # -- Global logging configuration logging: # -- The log level for greptimedb, only support "debug", "info", "warn", "debug" level: "info" # -- The log format for greptimedb, only support "json" and "text" format: "text" # -- The logs directory for greptimedb logsDir: "/data/greptimedb/logs" # -- Whether to log to stdout only onlyLogToStdout: false # -- indicates whether to persist the log with the datanode data storage. It **ONLY** works for the datanode component. persistentWithData: false # -- The log filters, use the syntax of `target[span\{field=value\}]=level` to filter the logs. filters: [] ``` Where: - `logging.level`: Configures the global log level, supports `debug`, `info`, `warn`, `error`. - `logging.format`: Configures the global log format, supports `json` and `text`. - `logging.logsDir`: Configures the global log directory, default `/data/greptimedb/logs`. - `logging.onlyLogToStdout`: Configures whether to output only to stdout, disabled by default. - `logging.persistentWithData`: Configures whether to persist logs with data storage, only applies to the `datanode` component, disabled by default. - `logging.filters`: Configures global log filters, supports the syntax `target[span\{field=value\}]=level`. For example, if you want to enable `debug` level logging for certain components: ```yaml logging: level: "info" format: "json" filters: - mito2=debug ``` Each role's logging configuration can be configured through the `${role}.logging` field, with fields consistent with the top-level `logging` and will **override** the top-level `logging` configuration, for example: ```yaml frontend: logging: level: "debug" ``` ### Enabling Flownode The Helm Chart does not enable Flownode by default. You can enable Flownode through the `flownode.enabled` field, as shown below: ```yaml flownode: enabled: true ``` Other fields of `flownode` are configured similarly to other Roles, for example: ```yaml flownode: enabled: true replicas: 1 podTemplate: main: resources: requests: memory: "1Gi" cpu: "1" limits: memory: "2Gi" cpu: "2" ``` ### Object Storage Configuration The `objectStorage` field is used to configure cloud object storage (such as AWS S3 and Azure Blob Storage, etc.) as the GreptimeDB storage layer. #### AWS S3 ```yaml objectStorage: credentials: # AWS access key ID accessKeyId: "" # AWS secret access key secretAccessKey: "" s3: # AWS S3 bucket name bucket: "" # AWS S3 region region: "" # The root path in bucket is 's3:////data/...' root: "" # The AWS S3 endpoint, see more detail: https://docs.aws.amazon.com/general/latest/gr/s3.html endpoint: "" ``` #### Using IAM Roles for Service Accounts (IRSA) for S3 Instead of providing static access keys, you can use [IAM Roles for Service Accounts](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) (IRSA) to grant S3 access to GreptimeDB. This approach is more secure as it eliminates the need to manage long-lived credentials. IRSA and [EKS Pod Identity](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html) are two different mechanisms. The configuration below is IRSA: it annotates the service account with the role ARN. Pod Identity instead requires a pod identity association and no such annotation. First, configure the datanode service account with the IAM role annotation. Only the datanode reads from and writes to S3: ```yaml datanode: podTemplate: serviceAccount: create: true annotations: eks.amazonaws.com/role-arn: ${YOUR_IAM_ROLE_ARN} ``` Make sure the IAM role has permissions to read and write to the target S3 bucket: - `s3:PutObject` - `s3:ListBucket` - `s3:GetObject` - `s3:DeleteObject` Then, configure the object storage without credentials: ```yaml objectStorage: s3: bucket: "${YOUR_S3_BUCKET}" region: "${YOUR_S3_REGION}" root: "greptimedb" ``` :::note When using IRSA, omit the `objectStorage.credentials` section entirely. The datanode pods will automatically obtain temporary credentials through the IAM role associated with the service account. ::: #### Google Cloud Storage ```yaml objectStorage: credentials: # GCP serviceAccountKey JSON-formatted base64 value serviceAccountKey: "" gcs: # Google Cloud Storage bucket name bucket: "" # Google Cloud OAuth 2.0 Scopes, example: "https://www.googleapis.com/auth/devstorage.read_write" scope: "" # The root path in bucket is 'gcs:////data/...' root: "" # Google Cloud Storage endpoint, example: "https://storage.googleapis.com" endpoint: "" ``` #### Azure Blob ```yaml objectStorage: credentials: # Azure account name accountName: "" # Azure account key accountKey: "" azblob: # Azure Blob container name container: "" # The root path in container is 'blob:////data/...' root: "" # Azure Blob endpoint, see: "https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-query-endpoint-srp?tabs=dotnet#query-for-the-blob-storage-endpoint" endpoint: "" ``` #### AliCloud OSS ```yaml objectStorage: credentials: # AliCloud access key ID accessKeyId: "" # AliCloud access key secret accessKeySecret: "" oss: # AliCloud OSS bucket name bucket: "" # AliCloud OSS region region: "" # The root path in bucket is 'oss:////data/...' root: "" # The AliCloud OSS endpoint endpoint: "" ``` #### Volcengine TOS TOS ([Torch Object Storage](https://www.volcengine.com/docs/6349)) is a massive, secure, cost-effective, user-friendly, highly reliable, and highly available object storage service provided by [Volcengine](https://www.volcengine.com). ```yaml objectStorage: credentials: # Volcengine access key ID accessKeyId: "" # Volcengine secret access key secretAccessKey: "" s3: # Volcengine TOS bucket name bucket: "" # Volcengine TOS region region: "" # The root path in bucket is 'tos:////data/...' root: "" # The Volcengine TOS endpoint, see more detail: https://www.volcengine.com/docs/6349/107356 endpoint: "" # Enable virtual host style so that OpenDAL will send API requests in virtual host style instead of path style. enableVirtualHostStyle: true ``` ### Prometheus Monitor Configuration If you have [prometheus-operator](https://github.com/prometheus-operator/prometheus-operator) installed, you can create Prometheus PodMonitor to monitor GreptimeDB through the `prometheusMonitor.enabled` field as follows: ```yaml prometheusMonitor: # -- Create PodMonitor resource for scraping metrics using PrometheusOperator enabled: false # -- Interval at which metrics should be scraped interval: "30s" # -- Add labels to the PodMonitor labels: release: prometheus ``` ### Debug Pod Configuration The debug pod has various tools installed (such as mysql-client, psql-client, etc.). You can exec into the debug pod for debugging. Create it with the `debugPod.enabled` field as follows: ```yaml debugPod: # -- Enable debug pod enabled: false # -- The debug pod image image: registry: docker.io repository: greptime/greptime-tool tag: "20250606-04e3c7d" # -- The debug pod resource resources: requests: memory: 64Mi cpu: 50m limits: memory: 256Mi cpu: 200m ``` ### Configuring Metasrv Backend Storage #### Using MySQL and PostgreSQL as Backend Storage You can configure the backend storage for the metasrv through the `meta.backendStorage` field. Let's take MySQL as an example. ```yaml meta: backendStorage: mysql: # -- MySQL host host: "mysql.default.svc.cluster.local" # -- MySQL port port: 3306 # -- MySQL database database: "metasrv" # -- MySQL table table: "greptime_metakv" # -- MySQL credentials credentials: # -- MySQL credentials secret name secretName: "meta-mysql-credentials" # -- MySQL credentials existing secret name existingSecretName: "" # -- MySQL credentials username username: "root" # -- MySQL credentials password password: "test" ``` - `mysql.host`: The MySQL host. - `mysql.port`: The MySQL port. - `mysql.database`: The MySQL database. - `mysql.table`: The MySQL table. - `mysql.credentials.secretName`: The MySQL credentials secret name. - `mysql.credentials.existingSecretName`: The MySQL credentials existing secret name. If you want to use an existing secret, you should make sure the secret contains the following keys: `username` and `password`. - `mysql.credentials.username`: The MySQL credentials username. It will be ignored if `mysql.credentials.existingSecretName` is set. The `username` will be stored in the `username` key of the secret with `mysql.credentials.secretName`. - `mysql.credentials.password`: The MySQL credentials password. It will be ignored if `mysql.credentials.existingSecretName` is set. The `password` will be stored in the `password` key of the secret with `mysql.credentials.secretName`. Most of the fields of `meta.backendStorage.postgresql` are the same as the fields of `meta.backendStorage.mysql`. For example: ```yaml meta: backendStorage: postgresql: # -- PostgreSQL host host: "postgres.default.svc.cluster.local" # -- PostgreSQL port port: 5432 # -- PostgreSQL database database: "metasrv" # -- PostgreSQL table table: "greptime_metakv" # -- PostgreSQL Advisory lock id used for election, shouldn't be used in other clusters or applications. electionLockID: 1 # -- PostgreSQL credentials credentials: # -- PostgreSQL credentials secret name secretName: "meta-postgresql-credentials" # -- PostgreSQL credentials existing secret name existingSecretName: "" # -- PostgreSQL credentials username username: "root" # -- PostgreSQL credentials password password: "root" ``` - `postgresql.host`: The PostgreSQL host. - `postgresql.port`: The PostgreSQL port. - `postgresql.database`: The PostgreSQL database. - `postgresql.table`: The PostgreSQL table. - `postgresql.electionLockID`: The Advisory lock id in PostgreSQL for election. - `postgresql.credentials.secretName`: The PostgreSQL credentials secret name. - `postgresql.credentials.existingSecretName`: The PostgreSQL credentials existing secret name. If you want to use an existing secret, you should make sure the secret contains the following keys: `username` and `password`. - `postgresql.credentials.username`: The PostgreSQL credentials username. It will be ignored if `postgresql.credentials.existingSecretName` is set. The `username` will be stored in the `username` key of the secret with `postgresql.credentials.secretName`. - `postgresql.credentials.password`: The PostgreSQL credentials password. It will be ignored if `postgresql.credentials.existingSecretName` is set. The `password` will be stored in the `password` key of the secret with `postgresql.credentials.secretName`. #### Using etcd as Backend Storage :::tip NOTE The configuration structure has changed between chart versions: - In older version: `meta.etcdEndpoints` - In newer version: `meta.backendStorage.etcd.endpoints` Always refer to the latest [values.yaml](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-cluster/values.yaml) in the Helm chart repository for the most up-to-date configuration structure. ::: The etcd backend storage can be configured through the `meta.backendStorage.etcd` field. ```yaml meta: backendStorage: etcd: # -- Etcd endpoints endpoints: ["etcd.etcd-cluster.svc.cluster.local:2379"] # -- Etcd store key prefix storeKeyPrefix: "" ``` - `etcd.endpoints`: The etcd endpoints. - `etcd.storeKeyPrefix`: The etcd store key prefix. All keys will be stored with this prefix. If you want to use one etcd cluster for multiple GreptimeDB clusters, you can configure different store key prefixes for each GreptimeDB cluster. It's only for testing and debugging purposes. ### Enable Region Failover You can enable Region Failover through the `meta.enableRegionFailover` field. Before enabling Region Failover, ensure your deployment meets the prerequisites outlined in the [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md) documentation. If your configuration does not meet the prerequisites, the **Operator will fail to deploy the cluster components**. ```yaml meta: enableRegionFailover: true ``` ### Enable IPv6 The `enableIPv6` field controls IPv6 support. Set it to true to enable IPv6 address allocation and communication. Defaults to false. Ensure that the underlying network infrastructure supports IPv6 before enabling this option. ```yaml enableIPv6: true ``` ### Enable GC Repartitioning depends on shared object storage and GC. You can enable GC on both metasrv and datanode with the following example: ```yaml meta: configData: | [gc] enable = true gc_cooldown_period = "5m" datanode: configData: | [[region_engine]] [region_engine.mito] [region_engine.mito.gc] enable = true lingering_time = "10m" unknown_file_lingering_time = "1d" ``` Make sure the datanode `lingering_time` is longer than the metasrv `gc_cooldown_period` to avoid deleting files that may still be in use. #### Enable Region Failover on Local WAL To enable Region Failover on local WAL, you need to set both `meta.enableRegionFailover: true` and add `allow_region_failover_on_local_wal = true` in the `meta.configData` field. :::warning WARNING Enabling Region Failover on local WAL may lead to data loss during failover. Ensure your operator version is greater than or equal to v0.2.2. ::: ```yaml meta: enableRegionFailover: true configData: | allow_region_failover_on_local_wal = true ``` ### Dedicated WAL Volume Configuring a dedicated WAL volume allows you to use a separate disk with a custom `StorageClass` for the WAL directory when deploying a GreptimeDB Datanode. ```yaml dedicatedWAL: enabled: true raftEngine: fs: storageClassName: io2 # Use aws ebs io2 storage class for WAL for better performance. name: wal storageSize: 20Gi mountPath: /wal ``` ### Enable Remote WAL To enable Remote WAL, both Metasrv and Datanode must be properly configured. Before proceeding, make sure to read the [Remote WAL Configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md) documentation for a complete overview of configuration options and important considerations. ```yaml remoteWal: enabled: true kafka: brokerEndpoints: ["kafka.kafka-cluster.svc:9092"] meta: configData: | [wal] provider = "kafka" replication_factor = 1 auto_prune_interval = "30m" datanode: configData: | [wal] provider = "kafka" overwrite_entry_start_id = true ``` --- ## Deploying a GreptimeDB Cluster with Frontend Groups In this guide, you will learn how to deploy a GreptimeDB cluster on Kubernetes with a frontend group consisting of multiple frontend instances. ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [GreptimeDB Operator](https://github.com/GrepTimeTeam/greptimedb-operator) >= v0.3.0 - [ETCD](https://github.com/bitnami/charts/tree/main/bitnami/etcd) ## Upgrade operator Install the GreptimeDB Operator, setting the image version to be greater than or equal to `v0.3.0`. For detailed instructions on upgrading the operator, please refer to the [GreptimeDB Operator Management](/user-guide/deployments-administration/deploy-on-kubernetes/greptimedb-operator-management.md#upgrade) guide. ## Frontend Groups Configuration :::tip NOTE The configuration structure has changed between chart versions: - In older version: `meta.etcdEndpoints` - In newer version: `meta.backendStorage.etcd.endpoints` Always refer to the latest [values.yaml](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-cluster/values.yaml) in the Helm chart repository for the most up-to-date configuration structure. ::: When configuring frontend groups, ensure that each group includes a `name` field. The following `values.yaml` example demonstrates how to define separate frontend groups for read and write operations: ```yaml frontend: enabled: false # Disable default frontend group frontendGroups: - name: read replicas: 1 config: | default_timezone = "UTC" [http] timeout = "60s" template: main: resources: limits: cpu: 2000m memory: 2048Mi - name: write replicas: 1 meta: replicas: 1 backendStorage: etcd: endpoints: - "etcd.etcd-cluster.svc.cluster.local:2379" datanode: replicas: 1 ``` You can use the following command to apply the configuration: ``` helm upgrade --install greptimedb greptime/greptimedb-cluster --namespace default -f values.yaml ``` ## Validity When setting the frontend groups, the name must be set. ```yaml frontendGroups: # - name: read #<=========The name must be set=============> - replicas: 1 ``` ## Verify the Installation Check the status of the pods: ```bash kubectl get pods -n default NAME READY STATUS RESTARTS AGE greptimedb-datanode-0 1/1 Running 0 32s greptimedb-flownode-0 1/1 Running 0 17s greptimedb-frontend-read-6d45bc9b89-hftqz 1/1 Running 0 23s greptimedb-frontend-write-557b6585c6-jq874 1/1 Running 0 23s greptimedb-meta-58cd4cff6c-zp7s9 1/1 Running 0 37s ``` To check the services: ```bash kubectl get service -n default NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE greptimedb-datanode ClusterIP None 4001/TCP,4000/TCP 102s greptimedb-flownode ClusterIP None 4001/TCP 2m5s greptimedb-frontend-read ClusterIP 10.96.174.200 4001/TCP,4000/TCP,4002/TCP,4003/TCP 42s greptimedb-frontend-write ClusterIP 10.96.223.1 4001/TCP,4000/TCP,4002/TCP,4003/TCP 42s greptimedb-meta ClusterIP 10.96.195.163 3002/TCP,4000/TCP 3m4s ``` ## Conclusion You have successfully deployed a GreptimeDB cluster with a frontend group consisting of read and write instances. You can now proceed to explore the functionality of your GreptimeDB cluster or integrate it with additional tools as needed. --- ## Deploying GreptimeDB Cluster with Remote WAL In this guide, you will learn how to deploy GreptimeDB with Remote WAL on Kubernetes. Before you start, it's recommended to read the [Deploy GreptimeDB Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md) guide first. ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 ## Dependencies Before deploying a GreptimeDB cluster with Remote WAL, ensure that the metadata storage and Kafka cluster are properly set up or that existing instances are available. - Metadata storage: you can refer to [Manage Metadata Overview](/user-guide/deployments-administration/manage-metadata/overview.md) for more details. In this example, we use etcd as the metadata storage. - Kafka Cluster: you can refer to [Manage Kafka](/user-guide/deployments-administration/wal/remote-wal/manage-kafka.md) for more details. ## Remote WAL Configuration :::tip NOTE The configuration structure has changed between chart versions: - In older version: `meta.etcdEndpoints` - In newer version: `meta.backendStorage.etcd.endpoints` Always refer to the latest [values.yaml](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-cluster/values.yaml) in the Helm chart repository for the most up-to-date configuration structure. ::: This example assumes you have a Kafka cluster running in the `kafka-cluster` namespace, and an etcd cluster running in the `etcd-cluster` namespace. The `values.yaml` file is as follows: ```yaml meta: backendStorage: etcd: endpoints: ["etcd.etcd-cluster.svc.cluster.local:2379"] configData: | [wal] provider = "kafka" # Testing value: no redundant copy. Set this from your durability policy in production. replication_factor = 1 topic_name_prefix = "gtp_greptimedb_wal_topic" auto_prune_interval = "30m" datanode: configData: | [wal] provider = "kafka" overwrite_entry_start_id = true remoteWal: enabled: true kafka: brokerEndpoints: ["kafka.kafka-cluster.svc.cluster.local:9092"] ``` ## Deploy GreptimeDB Cluster You can deploy the GreptimeDB cluster with the following command: ```bash helm upgrade --install mycluster \ --values values.yaml \ greptime/greptimedb-cluster \ -n default ``` ## Best Practices - **Avoid switching WAL storage options in an existing cluster**. If you need to change the WAL storage backend (e.g., from local to remote), you must **tear down the entire cluster** and perform a clean redeployment. This includes deleting: - All PersistentVolumeClaims (PVCs) used by the GreptimeDB cluster. - The object storage directory used by the cluster. - The metadata storage associated with the cluster. - Use a **minimal viable setup (MVP) to verify the cluster is functioning correctly**. This includes basic operations such as creating tables and inserting data to ensure the database works as expected. ## Next Steps - Follow the [Deploy GreptimeDB Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md) guide to access your GreptimeDB cluster. - Follow the [Quick Start](/getting-started/quick-start.md) guide to create tables and insert data. - For more information about Remote WAL configuration, see [Remote WAL Configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md). --- ## Deploy GreptimeDB Cluster In this guide, you will learn how to deploy a GreptimeDB cluster on Kubernetes using the GreptimeDB Operator. :::note The following output may have minor differences depending on the versions of the Helm charts and environment. ::: ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) >= v0.20.0 ## Create a test Kubernetes cluster :::warning Using `kind` is not recommended for production environments or performance testing. For such use cases, we recommend using cloud-managed Kubernetes services such as [Amazon EKS](https://aws.amazon.com/eks/), [Google GKE](https://cloud.google.com/kubernetes-engine/), or [Azure AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/), or deploying your own production-grade Kubernetes cluster. ::: There are many ways to create a Kubernetes cluster for testing purposes. In this guide, we will use [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) to create a local Kubernetes cluster. You can skip this step if you want to use the existing Kubernetes cluster. Here is an example using `kind` v0.20.0: ```bash kind create cluster ```
Expected Output ```bash Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.27.3) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! 😊 ```
Check the status of the cluster: ```bash kubectl cluster-info ```
Expected Output ```bash Kubernetes control plane is running at https://127.0.0.1:60495 CoreDNS is running at https://127.0.0.1:60495/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. ```
## Add the Greptime Helm repository We provide the [official Helm repository](https://github.com/GreptimeTeam/helm-charts) for the GreptimeDB Operator and GreptimeDB cluster. You can add the repository by running the following command: ```bash helm repo add greptime https://greptimeteam.github.io/helm-charts/ helm repo update ``` Check the charts in the Greptime Helm repository: ``` helm search repo greptime ```
Expected Output ```bash NAME CHART VERSION APP VERSION DESCRIPTION greptime/greptimedb-cluster 0.8.3 1.0.1 A Helm chart for deploying GreptimeDB cluster i... greptime/greptimedb-enterprise-dashboard 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB Enterpr... greptime/greptimedb-infra-test 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB infra t... greptime/greptimedb-operator 0.5.9 0.5.5 The greptimedb-operator Helm chart for Kubernetes. greptime/greptimedb-remote-compaction 0.1.1 0.1.0 Remote compaction components (scheduler, compac... greptime/greptimedb-standalone 0.4.2 1.0.1 A Helm chart for deploying standalone greptimedb ```
## Install and verify the GreptimeDB Operator It's ready to use Helm to install the GreptimeDB Operator on the Kubernetes cluster. ### Install the GreptimeDB Operator The [GreptimeDB Operator](https://github.com/GrepTimeTeam/greptimedb-operator) is a Kubernetes operator that manages the lifecycle of GreptimeDB cluster. Let's install the latest version of the GreptimeDB Operator in the `greptimedb-admin` namespace: ```bash helm install greptimedb-operator greptime/greptimedb-operator -n greptimedb-admin --create-namespace ```
Expected Output ```bash NAME: greptimedb-operator LAST DEPLOYED: Sun Apr 26 20:43:58 2026 NAMESPACE: greptimedb-admin STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: *********************************************************************** Welcome to use greptimedb-operator Chart version: 0.5.9 GreptimeDB Operator version: 0.5.5 *********************************************************************** Installed components: * greptimedb-operator The greptimedb-operator is starting, use `kubectl get deployment greptimedb-operator -n greptimedb-admin` to check its status. ```
:::note There is another way to install the GreptimeDB Operator by using `kubectl` and `bundle.yaml` from the latest release: ```bash kubectl apply -f \ https://github.com/GreptimeTeam/greptimedb-operator/releases/latest/download/bundle.yaml \ --server-side ``` This method is only suitable for quickly deploying GreptimeDB Operator in the test environments and is not recommended for production use. ::: ### Verify the GreptimeDB Operator installation Check the status of the GreptimeDB Operator: ```bash kubectl get pods -n greptimedb-admin -l app.kubernetes.io/instance=greptimedb-operator ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE greptimedb-operator-68d684c6cf-qr4q4 1/1 Running 0 4m8s ```
You also can check the CRD installation: ```bash kubectl get crds | grep greptimedb ```
Expected Output ```bash greptimedbclusters.greptime.io 2026-04-26T12:43:58Z greptimedbstandalones.greptime.io 2026-04-26T12:43:58Z ```
The GreptimeDB Operator will use `greptimedbclusters.greptime.io` and `greptimedbstandalones.greptime.io` CRDs to manage GreptimeDB cluster and standalone resources. ## Install the etcd cluster The GreptimeDB cluster requires an etcd cluster for metadata storage. Let's install an etcd cluster using Bitnami's etcd Helm [chart](https://github.com/bitnami/charts/tree/main/bitnami/etcd). ```bash helm install etcd \ oci://registry-1.docker.io/bitnamicharts/etcd \ --version 12.0.8 \ --set replicaCount=3 \ --set auth.rbac.create=false \ --set auth.rbac.token.enabled=false \ --create-namespace \ --set global.security.allowInsecureImages=true \ --set image.registry=docker.io \ --set image.repository=greptime/etcd \ --set image.tag=3.6.1-debian-12-r3 \ -n etcd-cluster ```
Expected Output ```bash NAME: etcd LAST DEPLOYED: Sun Apr 26 20:49:03 2026 NAMESPACE: etcd-cluster STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: CHART NAME: etcd CHART VERSION: 12.0.8 APP VERSION: 3.6.1 ** Please be patient while the chart is being deployed ** etcd can be accessed via port 2379 on the following DNS name from within your cluster: etcd.etcd-cluster.svc.cluster.local To create a pod that you can use as a etcd client run the following command: kubectl run etcd-client --restart='Never' --image docker.io/greptime/etcd:3.6.1-debian-12-r3 --env ETCDCTL_ENDPOINTS="etcd.etcd-cluster.svc.cluster.local:2379" --namespace etcd-cluster --command -- sleep infinity Then, you can set/get a key using the commands below: kubectl exec --namespace etcd-cluster -it etcd-client -- bash etcdctl put /message Hello etcdctl get /message To connect to your etcd server from outside the cluster execute the following commands: kubectl port-forward --namespace etcd-cluster svc/etcd 2379:2379 & echo "etcd URL: http://127.0.0.1:2379" WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs: - resources - preUpgradeJob.resources - disasterRecovery.cronjob.resources +info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ Substituted images detected: - docker.io/greptime/etcd:3.6.1-debian-12-r3 ```
Wait for the etcd cluster to be ready: ```bash kubectl get pods -n etcd-cluster -l app.kubernetes.io/instance=etcd ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE etcd-0 1/1 Running 0 2m8s etcd-1 1/1 Running 0 2m8s etcd-2 1/1 Running 0 2m8s ```
You can test the etcd cluster by running the following command: ```bash kubectl -n etcd-cluster \ exec etcd-0 -- etcdctl endpoint health \ --endpoints=http://etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379,http://etcd-1.etcd-headless.etcd-cluster.svc.cluster.local:2379,http://etcd-2.etcd-headless.etcd-cluster.svc.cluster.local:2379 ```
Expected Output ```bash http://etcd-1.etcd-headless.etcd-cluster.svc.cluster.local:2379 is healthy: successfully committed proposal: took = 3.008575ms http://etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379 is healthy: successfully committed proposal: took = 3.136576ms http://etcd-2.etcd-headless.etcd-cluster.svc.cluster.local:2379 is healthy: successfully committed proposal: took = 3.147702ms ```
## Setup `values.yaml` The `values.yaml` file contains parameters and configurations for GreptimeDB and is the key to defining the Helm chart. For example, a minimal GreptimeDB cluster configuration is as follows: ```yaml image: # Image registry: # Use `docker.io` for OSS GreptimeDB, # consult staff for Enterprise GreptimeDB registry: # Image repository: # Use `greptime/greptimedb` for OSS GreptimeDB, # consult staff for Enterprise GreptimeDB repository: # Image tag: # use database version for OSS GreptimeDB, for example, `v1.2.1` # consult staff for Enterprise GreptimeDB tag: pullSecrets: [] initializer: registry: docker.io repository: greptime/greptimedb-initializer tag: "v0.6.1" frontend: replicas: 1 meta: replicas: 1 backendStorage: etcd: endpoints: ["etcd.etcd-cluster.svc.cluster.local:2379"] datanode: replicas: 1 flownode: replicas: 1 ``` The configuration above for the GreptimeDB cluster is not recommended for production use. You should adjust the configuration according to your requirements. You can refer to the [configuration documentation](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) for the complete `values.yaml` configuration options. ## Install the GreptimeDB cluster Now that the GreptimeDB Operator and etcd cluster are installed, and `values.yaml` is configured, you can deploy a minimal GreptimeDB cluster: ```bash helm upgrade --install mycluster \ greptime/greptimedb-cluster \ --values /path/to/values.yaml \ -n default ```
Expected Output ```bash Release "mycluster" does not exist. Installing it now. NAME: mycluster LAST DEPLOYED: Sun Apr 26 21:00:40 2026 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: *********************************************************************** Welcome to use greptimedb-cluster Chart version: 0.8.3 GreptimeDB Cluster version: 1.0.1 *********************************************************************** Installed components: * greptimedb-meta * greptimedb-datanode * greptimedb-frontend * greptimedb-flownode The greptimedb-cluster is starting, use `kubectl get pods -n default` to check its status. ```
When starting the cluster installation, we can check the status of the GreptimeDB cluster with the following command. If you use a different cluster name and namespace, you can replace `mycluster` and `default` with your configuration: ```bash kubectl -n default get greptimedbclusters.greptime.io mycluster ```
Expected Output ```bash NAME FRONTEND DATANODE META FLOWNODE PHASE VERSION AGE mycluster 1 1 1 1 Running v1.0.1 111s ```
The above command will show the status of the GreptimeDB cluster. When the `PHASE` is `Running`, it means the GreptimeDB cluster has been successfully started. You also can check the Pods status of the GreptimeDB cluster: ```bash kubectl -n default get pods ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE mycluster-datanode-0 1/1 Running 0 2m51s mycluster-flownode-0 1/1 Running 0 2m26s mycluster-frontend-5894994974-w2cls 1/1 Running 0 2m32s mycluster-meta-58cd4cff6c-ddbxq 1/1 Running 0 2m58s ```
As you can see, we have created a minimal GreptimeDB cluster consisting of 1 frontend, 1 datanode, 1 flownode, and 1 metasrv by default. For information about the components of a complete GreptimeDB cluster, you can refer to [architecture](/user-guide/concepts/architecture.md). ## Explore the GreptimeDB cluster :::warning For production use, you should access the GreptimeDB cluster or Grafana inside the Kubernetes cluster or using the LoadBalancer type service. ::: ### Access the GreptimeDB cluster You can access the GreptimeDB cluster by using `kubectl port-forward` the frontend service: ```bash kubectl -n default port-forward svc/mycluster-frontend 4000:4000 4001:4001 4002:4002 4003:4003 ```
Expected Output ```bash Forwarding from 127.0.0.1:4000 -> 4000 Forwarding from [::1]:4000 -> 4000 Forwarding from 127.0.0.1:4001 -> 4001 Forwarding from [::1]:4001 -> 4001 Forwarding from 127.0.0.1:4002 -> 4002 Forwarding from [::1]:4002 -> 4002 Forwarding from 127.0.0.1:4003 -> 4003 Forwarding from [::1]:4003 -> 4003 ```
Please note that when you use a different cluster name and namespace, you can use the following command, and replace `${cluster}` and `${namespace}` with your configuration: ```bash kubectl -n ${namespace} port-forward svc/${cluster}-frontend 4000:4000 4001:4001 4002:4002 4003:4003 ``` :::warning If you want to expose the service to the public, you can use the `kubectl port-forward` command with the `--address` option: ```bash kubectl -n default port-forward --address 0.0.0.0 svc/mycluster-frontend 4000:4000 4001:4001 4002:4002 4003:4003 ``` Please make sure you have the proper security settings in place before exposing the service to the public. ::: Open the browser and navigate to `http://localhost:4000/dashboard` to access by the [GreptimeDB Dashboard](https://github.com/GrepTimeTeam/dashboard). If you want to use other tools like `mysql` or `psql` to connect to the GreptimeDB cluster, you can refer to the [Quick Start](/getting-started/quick-start.md). ## Cleanup :::danger The cleanup operation will remove the metadata and data of the GreptimeDB cluster. Please make sure you have backed up the data before proceeding. ::: ### Stop the port-forwarding Stop the port-forwarding for the GreptimeDB cluster: ```bash pkill -f kubectl port-forward ``` ### Uninstall the GreptimeDB cluster To uninstall the GreptimeDB cluster, you can use the following command: ```bash helm -n default uninstall mycluster ``` ### Delete the PVCs The PVCs wouldn't be deleted by default for safety reasons. If you want to delete the PV data, you can use the following command: ```bash kubectl -n default delete pvc -l app.greptime.io/component=mycluster-datanode ``` ### Cleanup the etcd cluster You can use the following command to clean up the etcd cluster: ```bash kubectl -n etcd-cluster exec etcd-0 -- etcdctl del "" --from-key=true ``` ### Destroy the Kubernetes cluster If you are using `kind` to create the Kubernetes cluster, you can use the following command to destroy the cluster: ```bash kind delete cluster ``` ## Next Steps If you want to deploy a GreptimeDB cluster with Remote WAL, you can refer to [Configure Remote WAL](/user-guide/deployments-administration/deploy-on-kubernetes/configure-remote-wal.md) for more details. --- ## Deploy GreptimeDB Infrastructure Tests In this guide, you will learn how to deploy GreptimeDB infrastructure testing tools on Kubernetes. ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) >= v0.20.0 ## Create a test Kubernetes cluster :::warning Using `kind` is not recommended for production environments or performance testing. For such use cases, we recommend using cloud-managed Kubernetes services such as [Amazon EKS](https://aws.amazon.com/eks/), [Google GKE](https://cloud.google.com/kubernetes-engine/), or [Azure AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/), or deploying your own production-grade Kubernetes cluster. ::: There are many ways to create a Kubernetes cluster for testing purposes. In this guide, we will use [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) to create a local Kubernetes cluster. You can skip this step if you want to use the existing Kubernetes cluster. Here is an example using `kind` v0.20.0: ```bash kind create cluster ```
Expected Output ```bash Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.27.3) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! 😊 ```
Check the status of the cluster: ```bash kubectl cluster-info ```
Expected Output ```bash Kubernetes control plane is running at https://127.0.0.1:60495 CoreDNS is running at https://127.0.0.1:60495/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. ```
## Add the Greptime Helm repository We provide the [official Helm repository](https://github.com/GreptimeTeam/helm-charts) for the GreptimeDB Operator and GreptimeDB cluster. You can add the repository by running the following command: ```bash helm repo add greptime https://greptimeteam.github.io/helm-charts/ helm repo update ``` Check the charts in the Greptime Helm repository: ``` helm search repo greptime ```
Expected Output ```bash NAME CHART VERSION APP VERSION DESCRIPTION greptime/greptimedb-cluster 0.8.3 1.0.1 A Helm chart for deploying GreptimeDB cluster i... greptime/greptimedb-enterprise-dashboard 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB Enterpr... greptime/greptimedb-infra-test 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB infra t... greptime/greptimedb-operator 0.5.9 0.5.5 The greptimedb-operator Helm chart for Kubernetes. greptime/greptimedb-remote-compaction 0.1.1 0.1.0 Remote compaction components (scheduler, compac... greptime/greptimedb-standalone 0.4.2 1.0.1 A Helm chart for deploying standalone greptimedb ```
## Configuration Create a custom configuration file `infra-test-values.yaml`: ```yaml image: # -- The image registry registry: docker.io # -- The image repository repository: greptime/greptime-tool # -- The image tag tag: "20260521-c19a702" # -- The image pull secrets pullSecrets: [] # -- Configure to the tests case: disk: enabled: true storageClass: null size: 20Gi cpu: enabled: true rds: enabled: true host: "your-rds-host" port: 3306 database: "test" username: "your-rds-username" password: "your-rds-password" s3: enabled: true bucket: "bucket-name" region: "s3-region" accessKeyID: "your-access-key-id" secretAccessKey: "your-secret-access-key" kafka: enabled: true endpoint: "your-kafka-endpoint" ``` ## Installation Install with custom configuration: ```bash helm upgrade --install greptimedb-infra-test greptime/greptimedb-infra-test \ --values infra-test-values.yaml \ -n default ``` ## View Test Results The testing tool runs as a Kubernetes job, with results output in the pod logs: ```bash kubectl get pod -n default ```
Expected output ```bash NAMESPACE NAME READY STATUS RESTARTS AGE default greptimedb-infra-test-n7z74 0/1 Completed 0 10m ```
```bash kubectl logs greptimedb-infra-test-n7z74 -n default ```
Expected output ```bash ================== Starting testing... ================== ================== Fetching UUID for kube-system namespace ================== Successfully got UUID: 419a9914-5f9c-4d47-af69-3d09789db7a3 ================== Disk tests ================== ================== Running full I/O test ================== seq-read: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64 seq-write: (g=0): rw=write, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=64 rand-iops: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=256 ... fio-3.28 Starting 6 processes seq-read: Laying out IO file (1 file / 1024MiB) seq-read: (groupid=0, jobs=6): err= 0: pid=14: Thu May 21 19:01:15 2026 read: IOPS=48.2k, BW=188MiB/s (198MB/s)(11.1GiB/60436msec) slat (nsec): min=625, max=615290k, avg=53392.68, stdev=2634423.19 clat (nsec): min=875, max=636646k, avg=7912987.95, stdev=30341131.85 lat (usec): min=52, max=636649, avg=7966.49, stdev=30463.52 clat percentiles (usec): | 1.00th=[ 223], 5.00th=[ 375], 10.00th=[ 562], 20.00th=[ 979], | 30.00th=[ 1303], 40.00th=[ 1680], 50.00th=[ 2311], 60.00th=[ 3490], | 70.00th=[ 4752], 80.00th=[ 6128], 90.00th=[ 13042], 95.00th=[ 23725], | 99.00th=[103285], 99.50th=[270533], 99.90th=[421528], 99.95th=[438305], | 99.99th=[488637] bw ( KiB/s): min= 9969, max=450583, per=99.87%, avg=192706.79, stdev=24021.38, samples=596 iops : min= 2490, max=112644, avg=48175.32, stdev=6005.33, samples=596 write: IOPS=34.0k, BW=133MiB/s (139MB/s)(8021MiB/60436msec); 0 zone resets slat (nsec): min=708, max=615509k, avg=77566.35, stdev=3387406.46 clat (usec): min=241, max=640859, avg=22321.94, stdev=54921.92 lat (usec): min=281, max=640862, avg=22399.63, stdev=55019.71 clat percentiles (msec): | 1.00th=[ 3], 5.00th=[ 5], 10.00th=[ 7], 20.00th=[ 8], | 30.00th=[ 9], 40.00th=[ 10], 50.00th=[ 11], 60.00th=[ 12], | 70.00th=[ 14], 80.00th=[ 16], 90.00th=[ 26], 95.00th=[ 54], | 99.00th=[ 359], 99.50th=[ 414], 99.90th=[ 485], 99.95th=[ 502], | 99.99th=[ 634] bw ( KiB/s): min=15032, max=284142, per=99.98%, avg=135879.94, stdev=12430.31, samples=596 iops : min= 3757, max=71033, avg=33968.77, stdev=3107.56, samples=596 lat (nsec) : 1000=0.01% lat (usec) : 20=0.01%, 50=0.01%, 100=0.01%, 250=0.95%, 500=3.99% lat (usec) : 750=3.41%, 1000=3.77% lat (msec) : 2=15.07%, 4=11.77%, 10=31.21%, 20=20.84%, 50=5.71% lat (msec) : 100=1.14%, 250=0.99%, 500=1.12%, 750=0.02% cpu : usr=1.88%, sys=8.37%, ctx=786665, majf=0, minf=263 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=100.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.1%, >=64=0.1% issued rwts: total=2915521,2053386,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=256 Run status group 0 (all jobs): READ: bw=188MiB/s (198MB/s), 188MiB/s-188MiB/s (198MB/s-198MB/s), io=11.1GiB (11.9GB), run=60436-60436msec WRITE: bw=133MiB/s (139MB/s), 133MiB/s-133MiB/s (139MB/s-139MB/s), io=8021MiB (8411MB), run=60436-60436msec Disk stats (read/write): vda: ios=2915375/2054793, merge=4/1130, ticks=8259465/6477837, in_queue=14772291, util=82.28% ================== Running mixed read/write test ================== fiotest: (g=0): rw=rw, bs=(R) 64.0KiB-64.0KiB, (W) 64.0KiB-64.0KiB, (T) 64.0KiB-64.0KiB, ioengine=libaio, iodepth=16 ... fio-3.28 Starting 8 processes fiotest: Laying out IO file (1 file / 1024MiB) fiotest: (groupid=0, jobs=8): err= 0: pid=22: Thu May 21 19:02:19 2026 read: IOPS=42.5k, BW=2657MiB/s (2786MB/s)(4092MiB/1540msec) slat (nsec): min=1541, max=3755.0k, avg=6912.45, stdev=28881.60 clat (usec): min=30, max=52156, avg=1045.12, stdev=2441.43 lat (usec): min=45, max=52191, avg=1052.13, stdev=2441.68 clat percentiles (usec): | 1.00th=[ 118], 5.00th=[ 188], 10.00th=[ 265], 20.00th=[ 392], | 30.00th=[ 506], 40.00th=[ 635], 50.00th=[ 783], 60.00th=[ 922], | 70.00th=[ 1074], 80.00th=[ 1254], 90.00th=[ 1680], 95.00th=[ 2311], | 99.00th=[ 3949], 99.50th=[ 4948], 99.90th=[47973], 99.95th=[49546], | 99.99th=[50070] bw ( MiB/s): min= 2578, max= 2871, per=100.00%, avg=2697.33, stdev=16.88, samples=24 iops : min=41246, max=45946, avg=43154.67, stdev=270.28, samples=24 write: IOPS=42.6k, BW=2662MiB/s (2792MB/s)(4100MiB/1540msec); 0 zone resets slat (usec): min=2, max=3271, avg= 8.33, stdev=25.30 clat (usec): min=95, max=52303, avg=1920.68, stdev=2554.97 lat (usec): min=138, max=52312, avg=1929.12, stdev=2555.52 clat percentiles (usec): | 1.00th=[ 310], 5.00th=[ 498], 10.00th=[ 693], 20.00th=[ 988], | 30.00th=[ 1254], 40.00th=[ 1516], 50.00th=[ 1762], 60.00th=[ 1975], | 70.00th=[ 2212], 80.00th=[ 2474], 90.00th=[ 2835], 95.00th=[ 3294], | 99.00th=[ 4817], 99.50th=[ 5800], 99.90th=[50594], 99.95th=[50594], | 99.99th=[50594] bw ( MiB/s): min= 2560, max= 2861, per=100.00%, avg=2701.42, stdev=15.97, samples=24 iops : min=40960, max=45780, avg=43219.67, stdev=255.59, samples=24 lat (usec) : 50=0.01%, 100=0.26%, 250=4.39%, 500=12.56%, 750=12.72% lat (usec) : 1000=12.92% lat (msec) : 2=34.10%, 4=21.45%, 10=1.28%, 20=0.01%, 50=0.23% lat (msec) : 100=0.06% cpu : usr=2.91%, sys=9.45%, ctx=35300, majf=0, minf=295 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=99.9%, 32=0.0%, >=64=0.0% submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0% issued rwts: total=65475,65597,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=16 Run status group 0 (all jobs): READ: bw=2657MiB/s (2786MB/s), 2657MiB/s-2657MiB/s (2786MB/s-2786MB/s), io=4092MiB (4291MB), run=1540-1540msec WRITE: bw=2662MiB/s (2792MB/s), 2662MiB/s-2662MiB/s (2792MB/s-2792MB/s), io=4100MiB (4299MB), run=1540-1540msec Disk stats (read/write): vda: ios=58387/58501, merge=35/82, ticks=56442/57649, in_queue=114196, util=49.76% ================== CPU tests ================== Architecture: aarch64 CPU op-mode(s): 64-bit Byte Order: Little Endian CPU(s): 6 On-line CPU(s) list: 0-5 Vendor ID: Apple Model: 0 Thread(s) per core: 1 Core(s) per cluster: 6 Socket(s): - Cluster(s): 1 Stepping: 0x0 BogoMIPS: 48.00 Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp flagm2 frint Vulnerability Gather data sampling: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Not affected Vulnerability Spec rstack overflow: Not affected Vulnerability Spec store bypass: Vulnerable Vulnerability Spectre v1: Mitigation; __user pointer sanitization Vulnerability Spectre v2: Not affected Vulnerability Srbds: Not affected Vulnerability Tsx async abort: Not affected ================== PostgreSQL Tests ================== ================== Running connection test ================== ✓ Connection successful PostgreSQL version: 17.5 ================== Running latency test ================== Query 1: 62.14ms Query 2: 32.75ms Query 3: 26.37ms Query 4: 27.06ms Query 5: 27.67ms ================== Running write performance test ================== DROP TABLE CREATE TABLE Testing single row INSERT performance (1000 rows)... Inserted 1000 rows in 50.45ms Average: .05ms per insert Testing bulk INSERT performance (10000 rows)... k Bulk inserted 10000 rows in 292.07ms Throughput: 34238 rows/sec ================== Running read performance test ================== Total rows: 10001 Count query took: 29.80ms SELECT with condition (id < 1000): 30.32ms Aggregate query (GROUP BY): 32.23ms ================== Running concurrent connection test ================== Spawned 20 concurrent connections (all completed) ================== Running database info query ================== Database size: 8531091 bytes Active connections: 6 PostgreSQL uptime: 2026-05-21 17:37:54.672924+00 ================== Running cleanup ================== DROP TABLE ================== PostgreSQL tests completed ================== ================== Generating 10MB test file... ================== 1+0 records in 1+0 records out 10485760 bytes (10 MB, 10 MiB) copied, 0.0255081 s, 411 MB/s ================== Running S3 transfer test... ================== ================== Upload s3 testfile... ================== 100.00% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.49 MB / 10.49 MB (491.69 kB/s) 22s (1/1) Script started on 2026-05-21 19:02:22+00:00 [] 100.00% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.49 MB / 10.49 MB (491.69 kB/s) 22s (1/1) Script done on 2026-05-21 19:02:44+00:00 [COMMAND_EXIT_CODE="0"] ================== Upload time: 22 seconds ================== ================== Download s3 testfile... ================== 100.00% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.49 MB / 10.49 MB (7.48 MB/s) 1.6s (1/1) Script started on 2026-05-21 19:02:44+00:00 [] 100.00% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.49 MB / 10.49 MB (7.48 MB/s) 1.6s (1/1) Script done on 2026-05-21 19:02:45+00:00 [COMMAND_EXIT_CODE="0"] ================== Download time: 1 seconds ================== ================== Verifying file integrity... ================== ================== S3 test passed ================== File size: 10485760 bytes MD5 checksum: 29022c552b0f81a9c89f6ff676a5c102 ================== Kafka test ================== ================== Creating Kafka Topic ================== ================== Starting Consumer ================== ================== Producing Messages ================== Produce time: 1 seconds ================== Consumed Messages ================== Successfully consumed 23 messages ================== All tests completed! ================== ```
--- ## Deploy GreptimeDB Standalone In this guide, you will learn how to deploy a GreptimeDB standalone on Kubernetes. :::note The following output may have minor differences depending on the versions of the Helm charts and environment. ::: ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) >= v0.20.0 ## Create a test Kubernetes cluster :::warning Using `kind` is not recommended for production environments or performance testing. For such use cases, we recommend using cloud-managed Kubernetes services such as [Amazon EKS](https://aws.amazon.com/eks/), [Google GKE](https://cloud.google.com/kubernetes-engine/), or [Azure AKS](https://azure.microsoft.com/en-us/services/kubernetes-service/), or deploying your own production-grade Kubernetes cluster. ::: There are many ways to create a Kubernetes cluster for testing purposes. In this guide, we will use [kind](https://kind.sigs.k8s.io/docs/user/quick-start/) to create a local Kubernetes cluster. You can skip this step if you want to use the existing Kubernetes cluster. Here is an example using `kind` v0.20.0: ```bash kind create cluster ```
Expected Output ```bash Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.27.3) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! 😊 ```
Check the status of the cluster: ```bash kubectl cluster-info ```
Expected Output ```bash Kubernetes control plane is running at https://127.0.0.1:60495 CoreDNS is running at https://127.0.0.1:60495/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. ```
## Add the Greptime Helm repository We provide the [official Helm repository](https://github.com/GreptimeTeam/helm-charts) for the GreptimeDB Operator and GreptimeDB cluster. You can add the repository by running the following command: ```bash helm repo add greptime https://greptimeteam.github.io/helm-charts/ helm repo update ``` Check the charts in the Greptime Helm repository: ``` helm search repo greptime ```
Expected Output ```bash NAME CHART VERSION APP VERSION DESCRIPTION greptime/greptimedb-cluster 0.8.3 1.0.1 A Helm chart for deploying GreptimeDB cluster i... greptime/greptimedb-enterprise-dashboard 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB Enterpr... greptime/greptimedb-infra-test 0.1.0 0.1.0 The Helm chart for deploying GreptimeDB infra t... greptime/greptimedb-operator 0.5.9 0.5.5 The greptimedb-operator Helm chart for Kubernetes. greptime/greptimedb-remote-compaction 0.1.1 0.1.0 Remote compaction components (scheduler, compac... greptime/greptimedb-standalone 0.4.2 1.0.1 A Helm chart for deploying standalone greptimedb ```
## Install the GreptimeDB Standalone ### Basic Installation For a quick start with default configuration: ```bash helm upgrade --install greptimedb-standalone greptime/greptimedb-standalone -n default ```
Expected Output ```bash Release "greptimedb-standalone" does not exist. Installing it now. NAME: greptimedb-standalone LAST DEPLOYED: Sun Apr 26 20:30:51 2026 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: *********************************************************************** Welcome to use greptimedb-standalone Chart version: 0.4.2 GreptimeDB Standalone version: 1.0.1 *********************************************************************** Installed components: * greptimedb-standalone The greptimedb-standalone is starting, use `kubectl get statefulset greptimedb-standalone -n default` to check its status. ```
```bash kubectl get pod -n default ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE greptimedb-standalone-0 1/1 Running 0 40s ```
### Customized Installation For production or customized deployments, create a `values.yaml` file: ```yaml resources: requests: cpu: "2" memory: "4Gi" limits: cpu: "4" memory: "8Gi" ``` For more configuration options, please refer to the [documentation](https://github.com/GreptimeTeam/helm-charts/tree/main/charts/greptimedb-standalone). Then install with custom values: ```bash helm upgrade --install greptimedb-standalone greptime/greptimedb-standalone \ --values values.yaml \ --namespace default ```
Expected Output ```bash Release "greptimedb-standalone" has been upgraded. Happy Helming! NAME: greptimedb-standalone LAST DEPLOYED: Sun Apr 26 20:35:11 2026 NAMESPACE: default STATUS: deployed REVISION: 2 TEST SUITE: None NOTES: *********************************************************************** Welcome to use greptimedb-standalone Chart version: 0.4.2 GreptimeDB Standalone version: 1.0.1 *********************************************************************** Installed components: * greptimedb-standalone The greptimedb-standalone is starting, use `kubectl get statefulset greptimedb-standalone -n default` to check its status. ```
```bash kubectl get pod -n default ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE greptimedb-standalone-0 1/1 Running 0 3s ```
## Access GreptimeDB After installation, you can access GreptimeDB through: ### MySQL Protocol ```bash kubectl port-forward svc/greptimedb-standalone 4002:4002 -n default > connections.out & ``` ```bash mysql -h 127.0.0.1 -P 4002 ```
Expected Output ```bash Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.4.2 Greptime Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]> ```
### PostgreSQL Protocol ```bash kubectl port-forward svc/greptimedb-standalone 4003:4003 -n default > connections.out & ``` ```bash psql -h 127.0.0.1 -p 4003 -d public ```
Expected Output ```bash psql (16.2, server 16.3-GreptimeDB-1.0.1) Type "help" for help. public=> ```
### HTTP API ```bash kubectl port-forward svc/greptimedb-standalone 4000:4000 -n default > connections.out & ``` ```bash curl -X POST \ -d 'sql=show databases' \ http://localhost:4000/v1/sql | jq . ```
Expected Output ```json { "output": [ { "records": { "schema": { "column_schemas": [ { "name": "Database", "data_type": "String" } ] }, "rows": [ [ "greptime_private" ], [ "information_schema" ], [ "public" ] ], "total_rows": 3 } } ], "execution_time_ms": 2 } ```
## Uninstallation To remove GreptimeDB standalone: ```bash helm uninstall greptimedb-standalone -n default ``` ```bash kubectl delete pvc -l app.kubernetes.io/instance=greptimedb-standalone -n default ``` --- ## Deploying Kafka Cluster In this guide, you will learn how to deploy a Kafka cluster on Kubernetes using a Helm Chart. ## Prerequisites - Kubernetes >= v1.18.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 ## Configuration Management Before installation, you need to create a configuration file for the Kafka cluster. Please adjust the following `kafka-values.yaml` reference configuration according to your Kubernetes environment: ```yaml image: registry: docker.io repository: greptime/kafka tag: 3.9.0-debian-12-r1 listeners: client: containerPort: 9092 protocol: PLAINTEXT name: CLIENT controller: protocol: PLAINTEXT heapOpts: "-Xmx512m -Xms512m -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M -XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80 -XX:+ExplicitGCInvokesConcurrent" controller: replicaCount: 3 resources: limits: cpu: '1' memory: 1Gi requests: cpu: 500m memory: 512Mi persistence: enabled: true storageClass: "" size: 50Gi broker: replicaCount: 3 resources: limits: cpu: '1' memory: 1Gi requests: cpu: 500m memory: 512Mi persistence: enabled: true storageClass: "" size: 50Gi extraConfig: | num.network.threads=3 num.io.threads=8 min.insync.replicas=1 socket.send.buffer.bytes=102400 socket.receive.buffer.bytes=102400 socket.request.max.bytes=104857600 num.recovery.threads.per.data.dir=1 offsets.topic.replication.factor=1 transaction.state.log.replication.factor=1 transaction.state.log.min.isr=1 allow.everyone.if.no.acl.found=true auto.create.topics.enable=true default.replication.factor=1 max.partition.fetch.bytes=1048576 max.request.size=1048576 message.max.bytes=20000000 log.dirs=/bitnami/kafka/data log.flush.interval.messages=10000 log.flush.interval.ms=1000 log.retention.hours=4 log.roll.hours=3 log.segment.bytes=1073741824 ``` :::warning Retention settings when this cluster backs Remote WAL `log.retention.hours=4` is a testing value. Kafka deletes a segment as soon as any retention condition matches, so a retention window shorter than your longest flush, failure, or manual-recovery window can remove WAL that GreptimeDB has not replayed yet. Do not add a size-based retention such as `log.retention.bytes` to topics used for Remote WAL. See [Required Settings and Limitations](/user-guide/deployments-administration/wal/remote-wal/configuration.md#required-settings-and-limitations) for the full set of constraints. ::: ## Installing Kafka Cluster Install the Kafka cluster in the kafka namespace: ```bash helm upgrade --install kafka \ --create-namespace \ oci://registry-1.docker.io/bitnamicharts/kafka \ --version 31.0.0 \ -n kafka --values kafka-values.yaml ```
Expected Output ```bash Release "kafka" does not exist. Installing it now. Pulled: greptime-registry.cn-hangzhou.cr.aliyuncs.com/charts/kafka:31.0.0 Digest: sha256:85b135981fd5d951ceef8b51cdcbc6917ebface50d0eb3367eb7ddc4a5db482b NAME: kafka LAST DEPLOYED: Tue May 12 00:57:32 2026 NAMESPACE: kafka STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: CHART NAME: kafka CHART VERSION: 31.0.0 APP VERSION: 3.9.0 ** Please be patient while the chart is being deployed ** Kafka can be accessed by consumers via port 9092 on the following DNS name from within your cluster: kafka.kafka.svc.cluster.local Each Kafka broker can be accessed by producers via port 9092 on the following DNS name(s) from within your cluster: kafka-controller-0.kafka-controller-headless.kafka.svc.cluster.local:9092 kafka-broker-0.kafka-broker-headless.kafka.svc.cluster.local:9092 To create a pod that you can use as a Kafka client run the following commands: kubectl run kafka-client --restart='Never' --image greptime-registry.cn-hangzhou.cr.aliyuncs.com/greptime/kafka:3.9.0-debian-12-r1 --namespace kafka --command -- sleep infinity kubectl exec --tty -i kafka-client --namespace kafka -- bash PRODUCER: kafka-console-producer.sh \ --bootstrap-server kafka.kafka.svc.cluster.local:9092 \ --topic test CONSUMER: kafka-console-consumer.sh \ --bootstrap-server kafka.kafka.svc.cluster.local:9092 \ --topic test \ --from-beginning Substituted images detected: - greptime-registry.cn-hangzhou.cr.aliyuncs.com/greptime/kafka:3.9.0-debian-12-r1 ```
## Verifying Kafka Cluster Installation Check the status of Kafka components (Broker and Controller): ```bash kubectl get pod -n kafka ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE kafka-broker-0 1/1 Running 0 8m3s kafka-broker-1 1/1 Running 0 8m2s kafka-broker-2 1/1 Running 0 8m1s kafka-controller-0 1/1 Running 0 8m3s kafka-controller-1 1/1 Running 0 8m2s kafka-controller-2 1/1 Running 0 8m1s ```
# Configuring Kafka Endpoints After the Kafka cluster is deployed, GreptimeDB can enable Remote WAL by configuring Kafka endpoints. For more information, refer to [this documentation](/user-guide/deployments-administration/deploy-on-kubernetes/configure-remote-wal.md). ```yaml remoteWal: enabled: true kafka: brokerEndpoints: - "kafka-broker-0.kafka-broker-headless.kafka.svc.cluster.local:9092" - "kafka-broker-1.kafka-broker-headless.kafka.svc.cluster.local:9092" - "kafka-broker-2.kafka-broker-headless.kafka.svc.cluster.local:9092" ``` # Monitoring - Install Prometheus Operator (eg: [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack))。 - Install the servicemonitor CRD。 To monitor the Kafka cluster, you need to have a monitoring system (such as Prometheus and Grafana) deployed in advance. Then add the following content to `kafka-values.yaml` and re-run the command to update the Kafka configuration: ```yaml metrics: jmx: enabled: true image: registry: greptime-registry.cn-hangzhou.cr.aliyuncs.com repository: greptime/jmx-exporter tag: 1.0.1-debian-12-r9 serviceMonitor: enabled: true namespace: "kafka" interval: "10s" labels: release: kube-prometheus-stack ``` ## Grafana dashboard Use [Kubernetes Kafka](https://grafana.com/grafana/dashboards/12483-kubernetes-kafka/) (ID: 12483) to monitor Kafka metrics. 1. Log in to your Grafana. 2. Navigate to Dashboards -> New -> Import. 3. Enter Dashboard ID: 12483, select a data source and load the dashboard. ![Kubernetes Kafka](/kubernetes-kafka-monitoring-dashboard.png) # Uninstalling Kafka Cluster Use the following command to uninstall the Kafka cluster: ```bash helm -n kafka uninstall kafka ``` ## Deleting PVCs Deleting PVCs will remove persistent data from the Kafka cluster. Please ensure you have backed up your data before proceeding. ```bash kubectl -n kafka delete pvc -l app.kubernetes.io/instance=kafka ``` --- ## Deploying MinIO Cluster In this guide, you will learn how to deploy a MinIO cluster on Kubernetes using a Helm Chart. ## Prerequisites - Kubernetes >= v1.18.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 ## Configuration Management Before installation, you need to create a `minio-values.yaml` configuration file. Please adjust the following configuration according to your Kubernetes environment: ```yaml global: security: allowInsecureImages: true image: registry: docker.io repository: greptime/minio tag: 2025.4.22-debian-12-r1 resources: requests: cpu: 500m memory: 500Mi limits: cpu: '2' memory: 2Gi extraEnvVars: - name: MINIO_REGION value: "ap-southeast-1" statefulset: replicaCount: 4 mode: distributed persistence: storageClass: null size: 100Gi ``` The root credentials are deliberately absent from this file. Helm reads a values file as plain YAML and does not expand shell variables in it, so a `${MINIO_ROOT_USER}` placeholder would be installed verbatim. Create a Secret and point the chart at it instead. Avoid `--set-string` for the password: Helm splits that value on commas itself, so a password containing one fails to parse no matter how the shell quotes it. ```bash kubectl create namespace minio kubectl -n minio create secret generic minio-root-credentials \ --from-literal=root-user="$MINIO_ROOT_USER" \ --from-literal=root-password="$MINIO_ROOT_PASSWORD" ``` Then reference it from `minio-values.yaml`: ```yaml auth: existingSecret: minio-root-credentials rootUserSecretKey: root-user rootPasswordSecretKey: root-password ``` These root credentials are for administering MinIO and signing in to its console. GreptimeDB does not use them — it connects with a separate Access Key that you create in [Generating Access Key](#generating-access-key), which you can scope with its own permission policy. ## Installing MinIO Cluster Install the MinIO cluster in the minio namespace: ```bash helm upgrade \ --install minio oci://registry-1.docker.io/bitnamicharts/minio \ --create-namespace \ --version 16.0.10 \ -n minio --values minio-values.yaml ```
Expected Output ```bash Release "minio" does not exist. Installing it now. Pulled: greptime-registry.cn-hangzhou.cr.aliyuncs.com/charts/minio:16.0.10 Digest: sha256:96e220fd7cf1596879a243453b39c96a95d34f0005fdd452da3d094a7b386eb4 NAME: minio LAST DEPLOYED: Tue May 12 17:21:30 2026 NAMESPACE: minio STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: CHART NAME: minio CHART VERSION: 16.0.10 APP VERSION: 2025.4.22 Did you know there are enterprise versions of the Bitnami catalog? For enhanced secure software supply chain features, unlimited pulls from Docker, LTS support, or application customization, see Bitnami Premium or Tanzu Application Catalog. See https://www.arrow.com/globalecs/na/vendors/bitnami for more information. ** Please be patient while the chart is being deployed ** MinIO® can be accessed via port 9000 on the following DNS name from within your cluster: minio.minio.svc.cluster.local To get your credentials run: export ROOT_USER=$(kubectl get secret --namespace minio minio -o jsonpath="{.data.root-user}" | base64 -d) export ROOT_PASSWORD=$(kubectl get secret --namespace minio minio -o jsonpath="{.data.root-password}" | base64 -d) To connect to your MinIO® server using a client: - Run a MinIO® Client pod and append the desired command (e.g. 'admin info'): kubectl run --namespace minio minio-client \ --rm --tty -i --restart='Never' \ --env MINIO_SERVER_ROOT_USER=$ROOT_USER \ --env MINIO_SERVER_ROOT_PASSWORD=$ROOT_PASSWORD \ --env MINIO_SERVER_HOST=minio \ --image docker.io/bitnami/minio-client:2025.4.16-debian-12-r1 -- admin info minio To access the MinIO® web UI: - Get the MinIO® URL: echo "MinIO® web URL: http://127.0.0.1:9001/minio" kubectl port-forward --namespace minio svc/minio 9001:9001 Substituted images detected: - greptime-registry.cn-hangzhou.cr.aliyuncs.com/greptime/minio:2025.4.22-debian-12-r1 ```
## Verifying MinIO Cluster Installation Check the status of MinIO Pods: ```bash kubectl get pod -n minio ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE minio-0 1/1 Running 0 30s minio-1 1/1 Running 0 30s minio-2 1/1 Running 0 30s minio-3 1/1 Running 0 30s ```
# Creating Bucket and Access Key ## Accessing MinIO Console 1. First, expose the MinIO console service. You can use the kubectl port-forward command: ```bash kubectl port-forward -n minio svc/minio 9001:9001 ``` 2. Open your browser: http://localhost:9001/login 3. Log in with the root credentials held in the `minio-root-credentials` Secret. ![MinIO login](/minio-login-page.png) ## Creating a Bucket After logging into the MinIO console, follow these steps to create a Bucket: 1. Click "Buckets" in the left sidebar 2. Click the "Create Bucket" button 3. Enter a Bucket name, for example:`greptimedb-bucket` 4. Click "Create Bucket" to confirm creation ![MinIO create bucket step 1](/minio-create-bucket-1.png) ![MinIO create bucket step 2](/minio-create-bucket-2.png) ## Generating Access Key 1. Click "Access Keys" in the left sidebar 2. Click the "Create Access Key" button 3. Optional: Set permission policies 4. Click "Create" to generate the Access Key and Secret Key ![MinIO create access key step 1](/minio-create-access-key-1.png) ![MinIO create access key step 2](/minio-create-access-key-2.png) :::warning ⚠️ Important: Please save the following information securely. You will need it when deploying GreptimeDB. - Bucket name:greptimedb-bucket - Region:ap-southeast-1 - MinIO Endpoint:`http://minio.minio:9000` - Access Key:The Access Key - Secret Key:The Secret Key ::: # Configuring GreptimeDB to Use MinIO When deploying a GreptimeDB cluster, you can use MinIO as backend storage with the following configuration: ```yaml objectStorage: credentials: accessKeyId: "" secretAccessKey: "" s3: bucket: "greptimedb-bucket" region: "ap-southeast-1" root: "greptimedb-data" endpoint: "http://minio.minio:9000" ``` # Monitoring - Install Prometheus Operator (e.g: [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack)). - Install the ServiceMonitor CRD. To monitor the MinIO cluster, you need to have a monitoring system (such as Prometheus and Grafana) deployed in advance. Then add the following content to minio-values.yaml and re-run the command to update the MinIO configuration: ```yaml metrics: enabled: true serviceMonitor: enabled: true namespace: minio labels: release: kube-prometheus-stack interval: 30s ``` ## Grafana dashboard Use the [MinIO Dashboard](https://grafana.com/grafana/dashboards/13502-minio-dashboard/) (ID: 13502) to monitor MinIO metrics. 1. Log in to your Grafana. 2. Navigate to Dashboards -> New -> Import. 3. Enter Dashboard ID: 13502, select a data source, and load the dashboard. ![MinIO Dashboard](/kubernetes-minio-monitoring-dashboard.png) # Uninstalling MinIO Cluster Use the following command to uninstall the MinIO cluster: ```bash helm -n minio uninstall minio ``` ## Deleting PVCs Deleting PVCs will remove persistent data from the MinIO cluster. Please ensure you have backed up your data before proceeding. ```bash kubectl -n minio delete pvc -l app.kubernetes.io/instance=minio ``` --- ## GreptimeDB Operator Management The GreptimeDB Operator manages the [GreptimeDB](https://github.com/GrepTimeTeam/greptimedb) resources on [Kubernetes](https://kubernetes.io/) using the [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). It is like an autopilot that automates the deployment, provisioning, and orchestration of the GreptimeDB cluster and standalone. The GreptimeDB Operator includes, but is not limited to, the following features: - **Automated Provisioning**: Automates the deployment of the GreptimeDB cluster and standalone on Kubernetes by providing CRD `GreptimeDBCluster` and `GreptimeDBStandalone`. - **Multi-Cloud Support**: Users can deploy the GreptimeDB on any Kubernetes cluster, including on-premises and cloud environments(like AWS, GCP, Aliyun, etc.). - **Scaling**: Scale the GreptimeDB cluster as easily as changing the `replicas` field in the `GreptimeDBCluster` CR. - **Monitoring Bootstrap**: Bootstrap the GreptimeDB monitoring stack for the GreptimeDB cluster by providing the `monitoring` field in the `GreptimeDBCluster` CR. This document will show you how to install, upgrade, configure, and uninstall the GreptimeDB Operator on Kubernetes. :::note The following output may have minor differences depending on the versions of the Helm charts and environment. ::: ## Prerequisites - Kubernetes >= v1.18.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 ## Production Deployment For production deployments, it's recommended to use Helm to install the GreptimeDB Operator. ### Installation You can refer [Install and verify the GreptimeDB Operator](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md#install-and-verify-the-greptimedb-operator) for detailed instructions. :::note If you are using [Argo CD](https://argo-cd.readthedocs.io/en/stable/) to deploy applications, please make sure that the `Application` has set the [`ServerSideApply=true`](https://argo-cd.readthedocs.io/en/latest/user-guide/sync-options/#server-side-apply) to enable the server-side apply (other GitOps tools may have similar settings). ::: ### Upgrade We always publish the latest version of the GreptimeDB Operator as a Helm chart in our official Helm repository. When the new version of the GreptimeDB Operator is released, you can upgrade the GreptimeDB Operator by running the following commands. #### Update the Helm repository ```bash helm repo update greptime ```
Expected Output ```bash Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "greptime" chart repository Update Complete. ⎈Happy Helming!⎈ ```
You can use the following command to search the latest version of the GreptimeDB Operator: ```bash helm search repo greptime/greptimedb-operator ```
Expected Output ```bash NAME CHART VERSION APP VERSION DESCRIPTION greptime/greptimedb-operator 0.5.9 0.5.5 The greptimedb-operator Helm chart for Kubernetes. ```
You also can use the following command to list all the available versions: ```bash helm search repo greptime/greptimedb-operator --versions ``` #### Upgrade the GreptimeDB Operator You can upgrade to the latest released version of the GreptimeDB Operator by running the following command: ```bash helm -n greptimedb-admin upgrade --install greptimedb-operator greptime/greptimedb-operator ```
Expected Output ```bash NAME: greptimedb-operator LAST DEPLOYED: Sun Apr 26 20:43:58 2026 NAMESPACE: greptimedb-admin STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: *********************************************************************** Welcome to use greptimedb-operator Chart version: 0.5.9 GreptimeDB Operator version: 0.5.5 *********************************************************************** Installed components: * greptimedb-operator The greptimedb-operator is starting, use `kubectl get deployment greptimedb-operator -n greptimedb-admin` to check its status. ```
If you want to upgrade to a specific version, you can use the following command: ```bash helm -n greptimedb-admin upgrade --install greptimedb-operator greptime/greptimedb-operator --version ``` After the upgrade is complete, you can use the following command to verify the installation: ```bash helm list -n greptimedb-admin ```
Expected Output ```bash NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION greptimedb-operator greptimedb-admin 1 2026-04-26 20:43:58.003167 +0800 CST deployed greptimedb-operator-0.5.9 0.5.5 ```
### CRDs There are two kinds of CRD that are installed with the GreptimeDB Operator: `GreptimeDBCluster` and `GreptimeDBStandalone`. You can use the following command to verify the installation: ```bash kubectl get crd | grep greptimedb ```
Expected Output ```bash greptimedbclusters.greptime.io 2026-04-26T12:43:58Z greptimedbstandalones.greptime.io 2026-04-26T12:43:58Z ```
By default, the GreptimeDB Operator chart will manage the installation and upgrade of the CRDs and the users don't need to manage them manually. If you want to know the specific definitions of these two types of CRD, you can refer to the GreptimeDB Operator [API documentation](https://github.com/GreptimeTeam/greptimedb-operator/blob/main/docs/api-references/docs.md). ### Configuration The GreptimeDB Operator chart provides a set of configuration options that allow you to customize the installation, you can refer to the [GreptimeDB Operator Helm Chart](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-operator/README.md#values) for more details. You can create a `values.yaml` to configure the GreptimeDB Operator chart (the complete configuration of `values.yaml` can be found in the [chart](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-operator/values.yaml)), for example: ```yaml image: # -- The image registry registry: docker.io # -- The image repository repository: greptime/greptimedb-operator # -- The image pull policy for the controller imagePullPolicy: IfNotPresent # -- The image tag tag: "v0.6.1" # -- The image pull secrets pullSecrets: [] replicas: 1 resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 256Mi ``` You also can use one command to install or upgrade the GreptimeDB Operator with the custom configuration: ```bash helm -n greptimedb-admin upgrade --install greptimedb-operator greptime/greptimedb-operator -f values.yaml ``` ### Uninstallation You can use the `helm` command to uninstall the GreptimeDB Operator: ```bash helm -n greptimedb-admin uninstall greptimedb-operator ``` We don't delete the CRDs by default when you uninstall the GreptimeDB Operator. :::danger If you really want to delete the CRDs, you can use the following command: ```bash kubectl delete crd greptimedbclusters.greptime.io greptimedbstandalones.greptime.io ``` The related resources will be removed after you delete the CRDs. ::: --- ## Deploy GreptimeDB on Kubernetes GreptimeDB runs on Kubernetes, either on-premises or on a cloud provider such as AWS, Alibaba Cloud, or Google Cloud. ## Deploy GreptimeDB Standalone For development, testing, or small-scale production use cases, you can [deploy a standalone GreptimeDB instance](deploy-greptimedb-standalone.md) on Kubernetes, without running a full cluster. ## Deploy GreptimeDB Cluster For production environments requiring high availability and horizontal scaling, [deploy a GreptimeDB cluster](deploy-greptimedb-cluster.md) using the GreptimeDB Operator on Kubernetes. ## Configurations You can apply custom configurations to GreptimeDB by creating a `values.yaml` file when deploying either GreptimeDB clusters or standalone instances. For a complete list of available configuration options, see [Common Helm Chart Configurations](./common-helm-chart-configurations.md). ## Manage GreptimeDB Operator The GreptimeDB Operator automates the setup, provisioning, and management of GreptimeDB instances on Kubernetes. Learn how to [manage the GreptimeDB Operator](./greptimedb-operator-management.md), including installation and upgrades. ## Advanced Deployments Once you are familiar with [the architecture and components of GreptimeDB](/user-guide/concepts/architecture.md), you can move on to these deployment scenarios: - [Deploy GreptimeDB Infrastructure test](deploy-greptimedb-infra-test.md): Prerequisite infrastructure testing for installing GreptimeDB. - [Deploy MinIO cluster](deploy-minio.md): Learn how to deploy, configure, and monitor a MinIO cluster. - [Deploy Kafka cluster](deploy-kafka.md): Learn how to deploy, configure, and monitor a Kafka cluster. - [Deploy GreptimeDB Cluster with Remote WAL](configure-remote-wal.md): Configure Kafka as a remote write-ahead log (WAL) for your GreptimeDB cluster, so writes that a Datanode has not yet flushed to storage can be replayed from Kafka after a failure. - [Use MySQL/PostgreSQL as Metadata Store](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#configuring-metasrv-backend-storage): Store cluster metadata in a MySQL or PostgreSQL database. - [Deploy Multi-Frontend GreptimeDB Cluster](configure-frontend-groups.md): Set up a GreptimeDB cluster on Kubernetes with a frontend group consisting of multiple frontend instances for improved load distribution and availability. --- ## Data Export & Import This guide describes how to use GreptimeDB's Export and Import tools for database data backup and restoration operations. For the resumable snapshot-based Export/Import V2 workflow, see [Export and Import Data with Export/Import V2](./export-import-v2.md). For detailed command-line options and advanced configurations, please refer to [Data Export & Import](/reference/command-lines/utilities/data.md). ## Export Operations ### Full Databases Backup Export every database into its own directory, including all tables and their data. ```bash # Export all databases backup greptime cli data export \ --addr localhost:4000 \ --output-dir /tmp/backup/greptimedb ``` The output directory structure is as follows: ``` / └── greptime/ └── / ├── create_database.sql ├── create_tables.sql ├── copy_from.sql └── ``` #### Export to S3 Export all databases backup to S3: ```bash greptime cli data export \ --addr localhost:4000 \ --s3 \ --s3-bucket \ --s3-access-key-id \ --s3-secret-access-key \ --s3-region \ --s3-root \ --s3-endpoint ``` ### Export with Basic Authentication If the GreptimeDB instance has authentication enabled, pass the credentials with `--auth-basic`: ```bash greptime cli data export \ --addr localhost:4000 \ --output-dir /tmp/backup/greptimedb \ --auth-basic : ``` ### Schema-Only Operations Export only schemas without data. This operation exports `CREATE TABLE` statements into SQL files, allowing you to backup table structures without the actual data. ```bash # Export only schemas greptime cli data export \ --addr localhost:4000 \ --output-dir /tmp/backup/schemas \ --target schema ``` ### Time-Range Based Backup ```bash # Export data within specific time range greptime cli data export --addr localhost:4000 \ --output-dir /tmp/backup/timerange \ --start-time "2024-01-01 00:00:00" \ --end-time "2024-01-31 23:59:59" ``` ### Specific Database Backup ```bash # To export a specific database greptime cli data export \ --addr localhost:4000 \ --output-dir /tmp/backup/greptimedb \ --database '{my_database_name}' ``` ## Import Operations ### Full Databases Import Import every database from a backup directory produced by `greptime cli data export`. ```bash # Import all databases greptime cli data import \ --addr localhost:4000 \ --input-dir /tmp/backup/greptimedb ``` ### Import with Basic Authentication If the GreptimeDB instance has authentication enabled, pass the credentials with `--auth-basic`: ```bash greptime cli data import \ --addr localhost:4000 \ --input-dir /tmp/backup/greptimedb \ --auth-basic : ``` ### Schema-Only Import Import only schemas without data. This operation imports `CREATE TABLE` statements from SQL files, allowing you to restore table structures without the actual data. ```bash # Import only schemas greptime cli data import \ --addr localhost:4000 \ --input-dir /tmp/backup/schemas \ --target schema ``` ### Specific Database Import ```bash # Import a specific database greptime cli data import \ --addr localhost:4000 \ --input-dir /tmp/backup/greptimedb \ --database '{my_database_name}' ``` ## Best Practices 1. **Parallelism Configuration** - Adjust `--export-jobs`/`--import-jobs` based on available system resources - Start with a lower value and increase gradually - Monitor system performance during operations 2. **Backup Strategy** - Incremental data backups using time ranges - Periodic backups for disaster recovery 3. **Error Handling** - Use `--max-retry` for handling transient failures - Keep logs for troubleshooting ## Performance Tips 1. **Export Performance** - Use time ranges for large datasets - Adjust parallel jobs based on CPU/Memory - Consider network bandwidth limitations 2. **Import Performance** - Monitor database resources ## Troubleshooting 1. **Connection Errors** - Verify server address and port - Check network connectivity - Ensure authentication credentials are correct 2. **Permission Issues** - Verify read/write permissions on output/input directories 3. **Resource Constraints** - Reduce parallel jobs - Ensure sufficient disk space - Monitor system resources during operations --- ## Metadata Export & Import This guide describes how to use GreptimeDB's metadata export and import tools for metadata backup and restoration operations. For detailed command-line options and advanced configurations, please refer to [Metadata Export & Import](/reference/command-lines/utilities/metadata.md). :::note - If Metasrv uses a PostgreSQL backend with `meta_schema_name` set, pass the same schema with `--meta-schema-name`. Without it, the CLI reads and writes the metadata table in the `search_path` of the connection, which may not be the table Metasrv uses. - `--backend-tls-mode` defaults to `disable`. If the metadata store requires TLS, set `--backend-tls-mode` and the related `--backend-tls-*` options. ::: ## Overview ## Export Operations ### Export to S3 Cloud Storage Export metadata from PostgreSQL to S3 for cloud-based backup storage: ```bash greptime cli meta snapshot save \ --store-addrs 'password=password dbname=postgres user=postgres host=localhost port=5432' \ --backend postgres-store \ --s3 \ --s3-bucket your-bucket-name \ --s3-region ap-southeast-1 \ --s3-access-key-id \ --s3-secret-access-key ``` **Output**: Creates `metadata_snapshot.metadata.fb` file in the specified S3 bucket. ### Export to Local Directory #### From PostgreSQL Backend Export metadata from PostgreSQL to local directory: ```bash greptime cli meta snapshot save \ --store-addrs 'password=password dbname=postgres user=postgres host=localhost port=5432' \ --backend postgres-store ``` #### From MySQL Backend Export metadata from MySQL to local directory: ```bash greptime cli meta snapshot save \ --store-addrs 'mysql://user:password@127.0.0.1:3306/database' \ --backend mysql-store ``` #### From etcd Backend Export metadata from etcd to local directory: ```bash greptime cli meta snapshot save \ --store-addrs 127.0.0.1:2379 \ --backend etcd-store ``` **Output**: Creates `metadata_snapshot.metadata.fb` file in the current working directory. #### From RaftEngine Backend :::note RaftEngine locks the metadata directory while the standalone instance is running. Stop the standalone instance before exporting. ::: Export metadata from RaftEngine to local directory: ```bash greptime cli meta snapshot save \ --store-addrs "raftengine:///path/to/metadata" \ --backend raft-engine-store ``` **Output**: Creates `metadata_snapshot.metadata.fb` file in the current working directory. ## Import Operations :::warning **Important**: Before importing metadata, ensure the target backend is in a **clean state** (contains no existing data). Importing to a non-empty backend may result in data corruption or conflicts. If the target backend is not empty and you do **not** pass `--force`, the command logs a warning and exits without restoring anything. It still returns a success exit code, so check the command output rather than assuming the restore ran. `--force` only bypasses the non-empty check. It does not clear the target first: the restore writes the keys contained in the snapshot and leaves any other existing keys in place, which is how a restored cluster ends up mixing two generations of metadata. Prefer restoring into an empty backend, then restart the whole cluster and verify catalogs, table routes, and procedures. ::: ### Import from S3 Cloud Storage Restore metadata from S3 backup to PostgreSQL storage backend: ```bash greptime cli meta snapshot restore \ --store-addrs 'password=password dbname=postgres user=postgres host=localhost port=5432' \ --backend postgres-store \ --s3 \ --s3-bucket your-bucket-name \ --s3-region ap-southeast-1 \ --s3-access-key-id \ --s3-secret-access-key ``` ### Import from Local File #### To PostgreSQL Backend Restore metadata from local backup file to PostgreSQL: ```bash greptime cli meta snapshot restore \ --store-addrs 'password=password dbname=postgres user=postgres host=localhost port=5432' \ --backend postgres-store ``` #### To MySQL Backend Restore metadata from local backup file to MySQL: ```bash greptime cli meta snapshot restore \ --store-addrs 'mysql://user:password@127.0.0.1:3306/database' \ --backend mysql-store ``` #### To etcd Backend Restore metadata from local backup file to etcd: ```bash greptime cli meta snapshot restore \ --store-addrs 127.0.0.1:2379 \ --backend etcd-store ``` #### To RaftEngine Backend Restore metadata from local backup file to RaftEngine: ```bash greptime cli meta snapshot restore \ --store-addrs "raftengine:///path/to/metadata" \ --backend raft-engine-store ``` --- ## DR Solution Based on Cross-Region Deployment in a Single Cluster ## How disaster recovery works in GreptimeDB GreptimeDB is well-suited for cross-region disaster recovery. You may have varying regional characteristics and business needs, and GreptimeDB offers tailored solutions to meet these diverse requirements. GreptimeDB resource management involves the concept of Availability Zones (AZs). An AZ is a logical unit of disaster recovery. It can be a Data Center (DC), a compartment of a DC. This depends on your specific DC conditions and deployment design. In the cross region disaster recovery solutions, a GreptimeDB region is a city. When two DC are in the same region and one DC becomes unavailable, the other DC can take over the services of the unavailable DC. This is a localization strategy. Before understanding the details of each DR solution, it is necessary to first understand the following knowledge: 1. The DR solution for the remote wal component is also very important. Essentially, it forms the foundation of the entire DR solution. Therefore, for each DR solution of GreptimeDB, we will let the remote wal component in the diagram. Currently, GreptimeDB's default remote wal component is implemented based on Kafka, and other implementations will be provided in the future; however, there won't be significant differences in deployment. 2. The table of GreptimeDB: Each table can be divided into multiple partitions according to a certain range, and each partition may be distributed on different datanodes. When writing or querying, the specified node will be called according to the corresponding rules. A table's partitions might look like this: ``` Table name: T1 Table partition count: 4 T1-1 T1-2 T1-3 T1-4 Table name: T2 Table partition count: 3 T2-1 T2-2 T2-3 ``` :::note The latencies listed under each solution below are example network round-trip values used to illustrate the topology, not measured GreptimeDB results. Measure the round-trip latency between your own AZs, DCs, and regions and substitute those numbers when comparing the solutions. ::: ### Metadata across 2 regions, data in the same region ![DR-across-2dc-1region](/DR-across-2dc-1region.svg) In this solution, the data is in one region (2 DCs), while the metadata across 2 regions. DC1 and DC2 are used together to handle read and write services, while DC3 (located in region2) is a replica used to meet the majority protocol. This architecture is also called the "2-2-1" solution. Both DC1 and DC2 must be able to handle all requests in extreme situations, so please ensure that sufficient resources are allocated. Latencies: - 2ms latency in the same region - 30ms latency in two regions Supports High Availability: - A single AZ is unavailable with the same performance - A single DC is unavailable with almost the same performance If you want a regional-level disaster recovery solution, you can take it a step further by providing read and write services on DC3. So, the next solution is: ### Data across 2 regions ![DR-across-3dc-2region](/DR-across-3dc-2region.svg) In this solution, the data across 2 regions. Each DC must be able to handle all requests in extreme situations, so please ensure that sufficient resources are allocated. Latencies: - 2ms latency in the same region - 30ms latency in two regions Supports High Availability: - A single AZ is unavailable with the same performance - A single DC is unavailable with degraded performance If you can't tolerate performance degradation from a single DC failure, consider upgrading to the five-DC and three-region solution. ### Metadata across 3 regions, data across 2 regions ![DR-across-5dc-2region](/DR-across-5dc-2region.svg) In this solution, the data across 2 regions, while the metadata across 3 regions. Region1 and region2 are used together to handle read and write services, while region3 is a replica used to meet the majority protocol. This architecture is also called the "2-2-1" solution. Each of the two adjacent regions must be able to handle all requests in extreme situations, so please ensure that sufficient resources are allocated. Latencies: - 2ms latency in the same region - 7ms latency in two adjacent regions - 30ms latency in two distant regions Supports High Availability: - A single AZ is unavailable with the same performance - A single DC is unavailable with the same performance - A single region(city) is unavailable with slightly degraded performance You can take it a step further by providing read and write services on both 3 regions. So, the next solution is: (This solution may have higher latency, so if that's unacceptable, it's not recommended.) ### Data across 3 regions ![DR-across-5dc-3region](/DR-across-5dc-3region.svg) In this solution, the data across 3 regions. In the event of a failure in one region, the other two regions must be able to handle all requests, so please ensure sufficient resources are allocated. Latencies: - 2ms latency in the same region - 7ms latency in two adjacent regions - 30ms latency in two distant regions Supports High Availability: - A single AZ is unavailable with the same performance - A single DC is unavailable with the same performance - A single region(city) is unavailable with degraded performance ## Solution Comparison The goal of the above solutions is to meet the high requirements for availability and reliability in medium to large-scale scenarios. However, in specific implementations, the cost and effectiveness of each solution may vary. The table below compares each solution to help you choose the final plan based on your specific scenario, needs, and costs. Here is the content formatted into a table: | Solution | Latencies | High Availability | | --- | --- | --- | | Metadata across 2 regions, data in the same region | - 2ms latency in the same region- 30ms latency in two regions | - A single AZ is unavailable with the same performance- A single DC is unavailable with almost the same performance | | Data across 2 regions | - 2ms latency in the same region- 30ms latency in two regions | - A single AZ is unavailable with the same performance- A single DC is unavailable with degraded performance | | Metadata across 3 regions, data across 2 regions | - 2ms latency in the same region- 7ms latency in two adjacent regions- 30ms latency in two distant regions | - A single AZ is unavailable with the same performance- A single DC is unavailable with the same performance- A single region(city) is unavailable with slightly degraded performance | | Data across 3 regions | - 2ms latency in the same region- 7ms latency in two adjacent regions- 30ms latency in two distant regions | - A single AZ is unavailable with the same performance - A single DC is unavailable with the same performance- A single region(city) is unavailable with degraded performance | --- ## DR solution for GreptimeDB Standalone --- ## Export and Import Data with Export/Import V2 Export/Import V2 creates portable snapshots of GreptimeDB data. Use it to back up data, restore data, or migrate data between GreptimeDB instances. A V2 snapshot contains schema metadata, a manifest, and data files. Data files are split into chunks so interrupted export and import jobs can resume from the previous progress. This guide uses the following commands: - `greptime cli data export-v2 create` to create a snapshot - `greptime cli data export-v2 verify` to verify a snapshot - `greptime cli data export-v2 list` to list snapshots under a parent location - `greptime cli data export-v2 delete` to delete a snapshot - `greptime cli data import-v2` to import from a snapshot ## Prerequisites Before using Export/Import V2, make sure that: - The GreptimeDB HTTP endpoint is reachable, for example `127.0.0.1:4000`. - You have a `greptime` binary that includes the `cli data export-v2` and `cli data import-v2` commands. - The snapshot storage location is readable and writable by both the CLI client and the GreptimeDB server. For remote object storage, explicitly enable one supported backend and provide the backend options. Export/Import V2 supports S3-compatible storage, Alibaba Cloud OSS, Google Cloud Storage, and Azure Blob Storage. For example, use `--s3` with `--s3-region` for S3-compatible storage. For `file://` snapshots, the path must be accessible from the GreptimeDB server as well as the CLI client. This usually means running the CLI on the same host as a standalone server, or mounting the same filesystem path into the GreptimeDB server. The parent directory of the snapshot must exist before you create the snapshot. For remote, Kubernetes, or distributed deployments, use object storage such as S3 or MinIO instead of a local `file://` path. The GreptimeDB server writes snapshot data files with `COPY DATABASE ... TO`, so a `file://` snapshot path must also be inside the server's copy root, which is `storage.copy_root` and defaults to `/copy`. The server rejects a path outside the copy root. Object storage snapshots are not subject to the copy root, because the server writes them directly to the object store. See [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for details. :::note The snapshot URI, such as `s3://my-bucket/snapshots/prod`, identifies the snapshot location, including the bucket. The object store options, such as `--s3-region` and `--s3-endpoint`, configure how the CLI connects to the backend. `--s3-bucket` and `--s3-root` do not change where the snapshot is read or written; the URI is authoritative. Pass the same connection options to create, verify, import, list, and delete commands. ::: ## Quick start with local filesystem Use a local filesystem snapshot only when the path is shared by the CLI client and the GreptimeDB server. For example, this works for a local standalone server when the CLI runs on the same host. The examples in this guide store snapshots under `/tmp/greptime-snapshots`. Set this directory as the server's copy root in the GreptimeDB configuration file and restart the server: ```toml [storage] copy_root = "/tmp/greptime-snapshots" ``` Alternatively, keep the default copy root and use a snapshot path under `/copy`, such as `file:///path/to/data_home/copy/demo`. Create the parent directory so that both the server and the CLI can access it: ```bash mkdir -p /tmp/greptime-snapshots ``` Create a snapshot: ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/demo ``` Verify the snapshot before restoring it: ```bash greptime cli data export-v2 verify \ --snapshot file:///tmp/greptime-snapshots/demo ``` Import from the snapshot: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/demo ``` By default, export and import use the `greptime` catalog. Add `--catalog ` if you need another catalog. ## Export to remote object storage Export/Import V2 can store snapshots in AWS S3, S3-compatible services such as MinIO, Alibaba Cloud OSS, Google Cloud Storage, and Azure Blob Storage. The examples below use the S3 backend because S3 and MinIO are common choices. For other object stores, use the corresponding backend flag and options described in [Supported storage backends](#supported-storage-backends). ### S3 or MinIO Use the S3 backend for AWS S3 and S3-compatible services such as MinIO. ### S3-compatible endpoint For MinIO, pass `--s3-endpoint`: ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to s3://greptime/snapshots/local-test \ --s3 \ --s3-bucket greptime \ --s3-region us-west-2 \ --s3-access-key-id superpower_ci_user \ --s3-secret-access-key superpower_password \ --s3-endpoint http://127.0.0.1:9000 ``` Use the same storage options when verifying, importing, listing, or deleting the snapshot: ```bash greptime cli data export-v2 verify \ --snapshot s3://greptime/snapshots/local-test \ --s3 \ --s3-bucket greptime \ --s3-region us-west-2 \ --s3-access-key-id superpower_ci_user \ --s3-secret-access-key superpower_password \ --s3-endpoint http://127.0.0.1:9000 ``` ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from s3://greptime/snapshots/local-test \ --s3 \ --s3-bucket greptime \ --s3-region us-west-2 \ --s3-access-key-id superpower_ci_user \ --s3-secret-access-key superpower_password \ --s3-endpoint http://127.0.0.1:9000 ``` For AWS S3, use the same `--s3` options but omit `--s3-endpoint` unless you use a custom endpoint. If your environment uses instance profiles or another credential provider, explicit access keys may not be required. The S3 backend still requires `--s3` and `--s3-region`. ## Export selected schemas By default, `export-v2 create` exports every schema except `information_schema`. This includes the `greptime_private` schema, and importing such a snapshot restores `greptime_private` as well. To exclude it, list the schemas you want explicitly with `--schemas`. Export only selected schemas with `--schemas`. You can pass a comma-separated list: ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/observability \ --schemas public,metrics ``` Or pass the option multiple times: ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/observability \ --schemas public \ --schemas metrics ``` When importing, `--schemas` selects a subset from the snapshot: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/observability \ --schemas public ``` If you request a schema that is not in the snapshot, the import fails. ## Export schema only Use `--schema-only` to export table definitions without data: ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/schema-only \ --schema-only ``` Importing a schema-only snapshot restores the schema but does not import data chunks: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/schema-only ``` ## Choose the data file format Use `--format` to choose the data file format. Supported values are `parquet`, `csv`, and `json`. The default is `parquet`. ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/demo-csv \ --format csv ``` The format is recorded in the snapshot manifest. `import-v2` reads the format from the manifest and applies it automatically, so you do not need to specify the format again when importing. ## Export by time range and chunk window Use a bounded time range and a chunk window for large datasets. Export/Import V2 creates one or more data chunks for each schema in the range. ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to s3://greptime/snapshots/prod-2026-06 \ --start-time 2026-06-01T00:00:00Z \ --end-time 2026-07-01T00:00:00Z \ --chunk-time-window 1d \ --s3 \ --s3-bucket greptime \ --s3-region us-west-2 \ --s3-access-key-id superpower_ci_user \ --s3-secret-access-key superpower_password \ --s3-endpoint http://127.0.0.1:9000 ``` `--chunk-time-window` requires both `--start-time` and `--end-time`. Chunks are created by time window. For example, a 15-minute range with `--chunk-time-window 5m` creates 3 chunks. During import, data tasks are scheduled per chunk and per schema. If the snapshot contains 3 chunks and 2 schemas with data, import schedules 6 data tasks. Snapshot data files are also organized by schema and chunk. For example, a multi-chunk snapshot can contain paths such as: ```text data/logs/1/app_logs.parquet data/logs/2/app_logs.parquet data/logs/3/app_logs.parquet data/public/1/host_metrics.parquet data/public/2/host_metrics.parquet data/public/3/host_metrics.parquet ``` Choose the chunk window based on the data volume and density: - Use a larger window for small or sparse datasets to avoid too many small chunks. - Use a smaller window for large datasets to keep each chunk manageable. - Start with `1d` and adjust after checking export duration and chunk sizes. ## Tune parallelism Export has two parallelism options: - `--parallelism` controls server-side `COPY DATABASE` parallelism per schema per chunk. The default is `1`. - `--chunk-parallelism` controls how many export chunks the client runs concurrently. The default is `1` and the valid range is `1..=64`. Import uses: - `--task-parallelism` to control how many import data tasks the client runs concurrently. The default is `1` and the valid range is `1..=64`. Increase these values gradually and monitor GreptimeDB, object storage, and network resource usage. ## Resume an interrupted export or import Export/Import V2 is designed to resume interrupted work. For export: - If the target snapshot does not exist, `export-v2 create` creates it. - If the target snapshot already exists, the same command resumes from existing progress. - Completed chunks are skipped. Failed or pending chunks are retried. - Use `--force` only when you want to delete the existing snapshot and recreate it. ```bash greptime cli data export-v2 create \ --addr 127.0.0.1:4000 \ --to file:///tmp/greptime-snapshots/demo ``` Run the same command again to resume after interruption. For import, progress is stored in a state file while the import is running. By default, the state file is under `~/.greptime/import_state`. Override it with `--state-path` when you want an explicit state location: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/demo \ --state-path /tmp/greptime-import-demo.state ``` `--dry-run` does not create an import state file. A real import keeps the state file if it fails or is interrupted, so the next run can resume completed data tasks. After a successful import, import-v2 automatically deletes the state file because there is no remaining work to resume. A `.lock` file with the same prefix can remain; it is a lock file, not the import state content. If you intentionally want to rerun an import from scratch after a failed or interrupted run, use a different `--state-path` or remove the previous state file after confirming that doing so is safe. ## Dry-run an import Use `--dry-run` to validate the snapshot and import plan without executing DDL or importing data: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/demo \ --dry-run ``` Dry-run is useful before a production migration or restore. ## Verify, list, and delete snapshots Verify a snapshot: ```bash greptime cli data export-v2 verify \ --snapshot file:///tmp/greptime-snapshots/demo ``` List snapshots under a parent location: ```bash greptime cli data export-v2 list \ --location file:///tmp/greptime-snapshots ``` Delete a snapshot: ```bash greptime cli data export-v2 delete \ --snapshot file:///tmp/greptime-snapshots/demo ``` For non-interactive deletion, pass `--no-confirm`: ```bash greptime cli data export-v2 delete \ --snapshot s3://greptime/snapshots/local-test \ --no-confirm \ --s3 \ --s3-bucket greptime \ --s3-region us-west-2 \ --s3-access-key-id superpower_ci_user \ --s3-secret-access-key superpower_password \ --s3-endpoint http://127.0.0.1:9000 ``` ## Authentication, timeout, proxy, and progress If the GreptimeDB instance has basic authentication enabled, pass credentials with `--auth-basic :`. Set request timeout with `--timeout`, for example `--timeout 60s` or `--timeout 5m`. For commands that connect to the GreptimeDB HTTP endpoint, such as `export-v2 create` and `import-v2`, use `--proxy` to set an HTTP proxy, or `--no-proxy` to disable proxy usage: ```bash greptime cli data import-v2 \ --addr 127.0.0.1:4000 \ --from file:///tmp/greptime-snapshots/demo \ --no-proxy ``` Long-running create and import commands support `--progress` to control progress output: - `auto`: show an interactive bar on a TTY, otherwise log progress. This is the default. - `always`: always emit progress; it uses a bar on a TTY and lightweight logs otherwise. - `never`: never emit progress. ## Supported storage backends Export/Import V2 supports local filesystem snapshots and remote object storage snapshots. For remote object storage, enable exactly one backend: - `--s3` for AWS S3 or S3-compatible storage such as MinIO - `--oss` for Alibaba Cloud OSS - `--gcs` for Google Cloud Storage - `--azblob` for Azure Blob Storage Common S3 options are: | Option | Description | | --- | --- | | `--s3-bucket` | S3 bucket name. Does not affect the snapshot location; the bucket in the snapshot URI is used. | | `--s3-region` | S3 region. Required for S3. | | `--s3-access-key-id` | Access key ID. Optional when the environment provides credentials. | | `--s3-secret-access-key` | Secret access key. Optional when the environment provides credentials. | | `--s3-endpoint` | Custom S3-compatible endpoint, such as a MinIO endpoint. | | `--s3-enable-virtual-host-style` | Enable virtual-hosted-style requests. | | `--s3-disable-ec2-metadata` | Disable EC2 metadata service credential lookup. | ## Troubleshooting ### Failed to connect to `/v1/sql` Check that GreptimeDB is running and that `--addr` points to the HTTP endpoint: ```bash curl http://127.0.0.1:4000/health ``` ### `file://` path is outside the configured copy root If `export-v2 create` fails with an error such as the following, the snapshot path is outside the server's copy root: ```text Local filesystem path '/tmp/greptime-snapshots/demo/data/greptime_private/1/' is outside the configured copy root or is unsafe ``` Set `storage.copy_root` to a directory that contains the snapshot path, or use a snapshot path under `/copy`. See [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for details. The failed run leaves a partial snapshot on disk, and `export-v2 verify` reports the failed chunk. After fixing the configuration, run the same `export-v2 create` command again to resume, or pass `--force` to recreate the snapshot. ### `--chunk-time-window` fails validation `--chunk-time-window` requires both `--start-time` and `--end-time`. Add a bounded time range or remove `--chunk-time-window`. ### S3 or MinIO reports `NoSuchBucket` or `NoSuchKey` Check that: - The bucket and path in the snapshot URI are correct. `--s3-bucket` does not change the snapshot location. - `--s3-endpoint` is set for MinIO or other S3-compatible services. Without it, the CLI sends requests to the public AWS S3 endpoint for `--s3-region`, which usually shows up as a long hang or a `301` response rather than a clear configuration error. - The credentials can read and write the snapshot location. - You pass the same storage options to create, verify, import, list, and delete commands. ### Import skips data unexpectedly Import resumes from its state file. If a previous import marked tasks as completed, a later run with the same state identity may skip them. Use an explicit `--state-path` for each restore operation, or remove the old state file after confirming it is safe. ### A requested schema is not found When using `--schemas` during import, make sure the schema exists in the snapshot. Use `export-v2 verify` first and check the snapshot contents. ### Verification fails Do not import a snapshot that fails verification. The snapshot may be incomplete or corrupted. Re-run the export with the same command to resume missing chunks, or recreate the snapshot with `--force` if you intentionally want to discard the existing snapshot. ## Limitations - Export/Import V2 snapshots are not the same format as the legacy export/import output. - Schema filtering is schema-level. Table-level filtering is not covered by this command. - Time-range chunking is designed for time-series data. Sparse data may need a larger chunk window. - Schema-only snapshots do not contain data files. - Large migrations require careful tuning of chunk window, parallelism, object storage throughput, and GreptimeDB resources. --- ## Disaster Recovery GreptimeDB is a distributed database designed to withstand disasters. It provides different solutions for disaster recovery (DR). This document contains: * Basic concepts in DR. * The deployment architecture of GreptimeDB and Backup & Restore (BR). * GreptimeDB provides the DR solutions. * Compares these DR solutions. ## Basic Concepts * **Recovery Time Objective (RTO)**: refers to the maximum acceptable amount of time that a business process can be down after a disaster occurs before it negatively impacts the organization. * **Recovery Point Objective (RPO)**: refers to the maximum acceptable amount of time since the last data recovery point. This determines what is considered an acceptable loss of data between the last recovery point and the interruption of service. The following figure illustrates these two concepts: ![RTO-RPO-explain](/RTO-RPO-explain.svg) * **Write-Ahead Logging (WAL)**: persistently records every data modification to ensure data integrity and consistency. GreptimeDB storage engine is a typical [LSM Tree](https://en.wikipedia.org/wiki/Log-structured_merge-tree) : ![LSM-tree-explain](/LSM-tree-explain.svg) The data written is going firstly persisted into WAL, then applied into Memtable in memory. Under specific conditions (e.g., exceeding the memory threshold), the Memtable will be flushed and persisted as an SSTable. So the DR of WAL and SSTable is key to the DR of GreptimeDB. * **Region**: a contiguous segment of a table, and also could be regarded as a partition in some relational databases. Read [Table Sharding](/contributor-guide/frontend/table-sharding.md#region) for more details. ## Component architecture ### GreptimeDB Before digging into the specific DR solution, let's explain the architecture of GreptimeDB components in the perspective of DR: ![Component-architecture](/Component-architecture.svg) GreptimeDB is designed with a cloud-native architecture based on storage-compute separation: * **Frontend**: the ingestion and query service layer, which forwards requests to Datanode and processes, and merges responses from Datanode. * **Datanode**: the storage layer of GreptimeDB, and is an LSM storage engine. Region is the basic unit for storing and scheduling data in Datanode. A region is a table partition, a collection of data rows. The data in region is saved into Object Storage (such as AWS S3). Unflushed Memtable data is written into WAL and can be recovered in DR. * **WAL**: persists the unflushed Memtable data in memory. It will be truncated when the Memtable is flushed into SSTable files. It can be local disk-based (local WAL) or Kafka cluster-based (remote WAL). * **Object Storage**: persists the SSTable data and index. The GreptimeDB stores data in object storage such as [AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/DataDurability.html) or its compatible services, which is designed to provide 99.999999999% durability and 99.99% availability of objects over a given year. And services such as S3 provide [replications in Single-Region or Cross-Region](https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication.html), which is naturally capable of DR. At the same time, the WAL component is pluggable, e.g. using Kafka as the WAL service that offers a mature [DR solution](https://www.confluent.io/blog/disaster-recovery-multi-datacenter-apache-kafka-deployments/). ### Backup and restore ![BR-explain](/BR-explain.svg) The Backup & Restore (BR) tool can perform a full snapshot backup of databases or tables at a specific time and supports incremental backup. When a cluster encounters a disaster, you can restore the cluster from backup data. Generally speaking, BR is the last resort for disaster recovery. ## Solutions introduction ### DR solution for GreptimeDB Standalone If the Standalone is running on the local disk for WAL and data, then: * RPO: depends on backup frequency. * RTO: doesn't make sense in standalone mode, mostly depends on the size of the data to be restored, your failure response time, and the operational infrastructure. A good start is to deploy GreptimeDB Standalone into an IaaS platform that has a backup and recovery solution. For example, Amazon EC2 with EBS volumes provides a comprehensive [Backup and Recovery solution](https://docs.aws.amazon.com/prescriptive-guidance/latest/backup-recovery/backup-recovery-ec2-ebs.html). But if running the Standalone with remote WAL and object storage, there is a better DR solution: ![DR-Standalone](/DR-Standalone.svg) Write the WAL to the Kafka cluster and store the data in object storage, so that the ingested data no longer depends on the node's local disk. The node is not fully stateless, though: a standalone instance keeps its metadata — catalogs, schemas and table definitions — in a local key-value store under `/metadata` (see [Storage Location](/user-guide/concepts/storage-location.md)). Kafka and object storage cannot rebuild it. Losing the host together with its disk means losing that metadata unless you have backed it up separately, so include it in the plan with [Metadata Export & Import](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md). **RPO=0** and an **RTO in minutes** are the design targets of this topology. They hold as long as the Kafka cluster and the object storage both survive the failure you are planning for, the WAL covering unflushed writes is still present, and the metadata can be restored. Verify them with a failure drill against your own deployment. ### DR solution based on single-region deployment in a single cluster ![Single-region-single-cluster](/Single-region-single-cluster.svg) Before a cluster spans regions, it has to survive the loss of a single node or of a single AZ inside one region. An AZ here is the same logical unit of disaster recovery used in the cross-region solutions: a data center, or a compartment of a data center. This topology is the common production baseline, and the cross-region solutions below are built on top of it. The cluster lives in one region, with its roles spread over the AZs that region offers: * **Frontend** is stateless. Run several replicas behind a load balancer so that a failed replica only affects the requests in flight on it. See [Role Replicas Configuration](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#role-replicas-configuration). * **Metasrv** runs as several replicas that elect a leader; losing a follower is transparent, and losing the leader costs a re-election. The metadata itself lives in an external backend — etcd, MySQL or PostgreSQL — that Metasrv does not replicate for you, so give that backend its own high availability and its own backups. See [Metadata Management](/user-guide/deployments-administration/manage-metadata/overview.md) and [Metadata Export & Import](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md). * **Datanode** holds the regions. When one fails, [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md) reopens its regions on the surviving Datanodes. It is **disabled by default**, and it requires [shared storage](/user-guide/deployments-administration/configuration.md#storage-options) together with remote WAL. Enabling it on local WAL with `allow_region_failover_on_local_wal=true` is possible but may lose data, because the WAL of the failed node stays on that node's disk. * **Kafka** (remote WAL) and the **object storage** hold the state that has to outlive the node. Give Kafka a replication factor that tolerates the broker loss you are planning for, and spread brokers and Datanodes over the AZs instead of packing them into one. Latencies: - Same-region round trips only; neither writes nor replication pay a cross-region penalty Supports High Availability: - A single node is unavailable with almost the same performance, once its regions are reopened elsewhere - A single AZ is unavailable with degraded performance, unless the surviving AZs were sized to absorb its share of the load - The region itself is not covered; it is a single failure domain in this topology This solution targets zero RPO and a second- or minute-level RTO for a node or AZ failure. As with the other solutions, the numbers depend on conditions you have to check: - Region Failover is **disabled by default** and must be enabled explicitly. - The surviving Datanodes need spare capacity to carry the failed node's regions, otherwise failover only moves the overload. - The recovery time is dominated by WAL replay: the more regions share one Kafka topic, the more redundant data has to be read before those regions serve again. Read [The recovery time of Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md#the-recovery-time-of-region-failover) for the model behind that. Confirm the resulting RPO and RTO with a failure drill. If the region as a whole, the Kafka cluster or the object storage becomes unavailable, you need one of the solutions below, or regular backups kept in another region. ### DR solution based on Active-Active Failover ![Active-active failover](/active-active-failover.svg) In some edge or small-to-medium scale scenarios, or if you lack the resources to deploy remote WAL or object storage, Active-Active Failover offers a better solution compared to Standalone DR. Two actively serving standalone nodes replicate data changes asynchronously. If a peer or the inter-site network fails, the healthy node continues serving, retains pending changes on its local storage, and sends them after the peer recovers. Pending changes remain recoverable only while the source node's local storage is available. Losing a node and its local storage before replication completes can leave the peer without those changes. Deploying nodes in different regions can also meet region-level DR requirements, but the scalability is limited. :::tip NOTE **Active-Active Failover is only available in GreptimeDB Enterprise.** ::: For more information about this solution, see [DR solution based on Active-Active Failover](/enterprise/deployments-administration/disaster-recovery/dr-solution-based-on-active-active-failover.md). ### DR solution based on cross-region deployment in a single cluster ![Cross-region-single-cluster](/Cross-region-single-cluster.svg) For medium-to-large scale scenarios requiring zero RPO, this solution is highly recommended. In this deployment architecture, the entire cluster spans across three regions, with each region capable of handling both read and write requests. Data replication is achieved using remote WAL and object storage, both of which must have cross-region DR enabled. If Region 1 becomes completely unavailable due to a disaster, the table regions within it will be opened and recovered in the other regions. In the event that Region 1 becomes completely unavailable due to a disaster, the table regions within it will be opened and recovered in the other regions. Region 3 serves as a replica to adhere to the majority protocol of Metasrv. This solution targets region-level fault tolerance, scalable write capability, zero RPO, and a second- or minute-level RTO. Reaching those numbers depends on the whole dependency chain, not on the cluster layout alone: - Region Failover is **disabled by default** and must be enabled explicitly. - Kafka, the object storage, the metadata backend and the traffic entry point all have to span the failure domain you are protecting against. Metasrv running in three regions does not replicate an external MySQL or PostgreSQL metadata backend for you. - The automatic Datanode selector picks targets by round-robin, lease or load. It is not an availability-zone-aware placement policy, so surviving regions need enough healthy capacity to take over. Confirm the resulting RPO and RTO with an end-to-end failure drill. For more information about this solution, see [DR solution based on cross-region deployment in a single cluster](./dr-solution-based-on-cross-region-deployment-in-single-cluster.md). ### DR solution based on BR ![/BR-DR](/BR-DR.svg) In this architecture, GreptimeDB Cluster 1 is deployed in region 1. The BR process continuously and regularly backs up the data from Cluster 1 to region 2. If region 1 experiences a disaster rendering Cluster 1 unrecoverable, you can use the backup data to restore a new cluster (Cluster 2) in region 2 to resume services. The DR solution based on BR provides an RPO depending on the backup frequency and an RTO that varies with the size of the data to be restored. Read [Backup & restore data](./back-up-&-restore-data.md) for details. ### Solution Comparison By comparing these DR solutions, you can decide on the final option based on their specific scenarios, requirements, and cost. The RPO and RTO columns are the design targets of each topology under the conditions described above, not values guaranteed for every deployment; confirm them with a failure drill. | DR solution | Error Tolerance Objective | RPO | RTO | TCO | Scenarios | Remote WAL & Object Storage | Notes | | ------------- | ------------------------- | ----- | ----- | ----- | ---------------- | --------- | --------| | DR solution for Standalone| Single-Region | Backup Interval | Minute or Hour level | Low | Low requirements for availability and reliability in small scenarios | Optional | | | DR solution based on single-region deployment in a single cluster | Single-Region, node and AZ level | 0 | Second or minute level | Medium | The common production baseline for a cluster that lives in one region | Required | Region Failover is disabled by default | | DR solution based on Active-Active Failover | Cross-Region | Depends on pending changes and the failure mode | Depends on external failover | Low | High requirements for availability and reliability in small-to-medium scenarios | Optional | Commercial feature | | DR solution based on cross-region deployment in a single cluster| Multi-Regions | 0 | Second or minute level | High | High requirements for availability and reliability in medium-to-large scenarios | Required | | | DR solution based on BR | Single-Region | Backup Interval | Minute or Hour level | Low | Acceptable requirements for availability and reliability | Optional | | ## References * [Backup & restore data](./back-up-&-restore-data.md) * [DR solution based on Active-Active Failover ](/enterprise/deployments-administration/disaster-recovery/dr-solution-based-on-active-active-failover.md) * [DR solution based on cross-region deployment in a single cluster](./dr-solution-based-on-cross-region-deployment-in-single-cluster.md) * [S3 Replicating objects overview](https://docs.aws.amazon.com/AmazonS3/latest/userguide/replication.html) --- ## Cluster Maintenance Mode Maintenance mode is a safety feature in GreptimeDB that temporarily disables automatic cluster management operations. This mode is particularly useful during: - Cluster deployment - Cluster upgrades - Planned downtime - Any operation that might temporarily affect cluster stability ## When to Use Maintenance Mode ### With GreptimeDB Operator If you are upgrading a cluster using GreptimeDB Operator, you don't need to enable the maintenance mode manually. The operator handles this automatically. ### Without GreptimeDB Operator When upgrading a cluster without using GreptimeDB Operator, **you must manually enable Metasrv's maintenance mode before**: 1. Deploying a new cluster (maintenance mode should be enabled after metasrv nodes are ready) 2. Rolling upgrades of Datanode nodes 3. Metasrv nodes upgrades 4. Frontend nodes upgrades 5. Any operation that might cause temporary node unavailability After the cluster is deployed/upgraded, you can disable the maintenance mode. ## Impact of Maintenance Mode When maintenance mode is enabled: - Auto Balancing (if enabled) will be paused - Region Failover (if enabled) will be paused - Manual region operations are still possible - Read and write operations continue to work normally - Monitoring and metrics collection continue to function ## Managing Maintenance Mode The maintenance mode can be enabled and disabled through Metasrv's HTTP interface at: `http://{METASRV}:{HTTP_PORT}/admin/maintenance/enable` and `http://{METASRV}:{HTTP_PORT}/admin/maintenance/disable`. Note that this interface listens on Metasrv's `HTTP_PORT`, which defaults to `4000`. ### Enable Maintenance Mode :::danger After calling the maintenance mode interface, ensure you check that the HTTP status code returned is 200 and confirm that the response content meets expectations. If there are any exceptions or the interface behavior does not meet expectations, proceed with caution and avoid continuing with high-risk operations such as cluster upgrades. ::: Enable maintenance mode by sending a POST request to the `/admin/maintenance/enable` endpoint. ```bash curl -X POST 'http://localhost:4000/admin/maintenance/enable' ``` The expected output is: ```bash {"enabled":true} ``` If you encounter any issues or unexpected behavior, do not proceed with maintenance operations. ### Disable Maintenance Mode :::danger Before disabling maintenance mode, you must confirm that **all components have returned to normal status**. ::: Disable maintenance mode by sending a POST request to the `/admin/maintenance/disable` endpoint. Before disabling maintenance mode: 1. Ensure all components are healthy and operational 2. Verify that all nodes are properly joined to the cluster ```bash curl -X POST 'http://localhost:4000/admin/maintenance/disable' ``` The expected output is: ```bash {"enabled":false} ``` ### Check Maintenance Mode Status Check maintenance mode status by sending a GET request to the `/admin/maintenance/status` endpoint. ```bash curl -X GET http://localhost:4000/admin/maintenance/status ``` The expected output is: ```bash {"enabled":false} ``` ## Troubleshooting ### Common Issues 1. **Maintenance mode cannot be enabled** - Verify Metasrv is running and accessible - Check if you have the correct permissions - Ensure the HTTP port is correct ### Best Practices 1. Always verify the maintenance mode status before and after operations 2. Have a rollback plan ready 3. Monitor cluster health during maintenance 4. Document all changes made during maintenance --- ## Prevent Metadata Changes To prevent metadata changes, you can pause the Procedure Manager. This mechanism rejects all new procedures (i.e., new metadata-changing operations) while allowing existing procedures to continue running. Once enabled, the Metasrv will reject the following procedures (including but not limited to): **DDL procedures:** - Create table - Drop table - Alter table - Create database - Drop database - Create view - Create flow - Drop flow **Region procedures:** - Region Migration - Region Failover (if enabled) - Auto Balancing (if enabled) You may see error messages if you or Metasrv try to perform these procedures after metadata changes are paused. For region procedures, you can enable [Cluster Maintenance Mode](/user-guide/deployments-administration/maintenance/maintenance-mode.md) to temporarily disable them. ## Managing Procedure Manager The Procedure Manager can be paused and resumed through Metasrv's HTTP interface at: `http://{METASRV}:{HTTP_PORT}/admin/procedure-manager/pause` and `http://{METASRV}:{HTTP_PORT}/admin/procedure-manager/resume`. Note that this interface listens on Metasrv's `HTTP_PORT`, which defaults to `4000`. ### Pause Procedure Manager Pause Procedure Manager by sending a POST request to the `/admin/procedure-manager/pause` endpoint. ```bash curl -X POST 'http://localhost:4000/admin/procedure-manager/pause' ``` The expected output is: ```bash {"status":"paused"} ``` ### Resume Procedure Manager Resume Procedure Manager by sending a POST request to the `/admin/procedure-manager/resume` endpoint. ```bash curl -X POST 'http://localhost:4000/admin/procedure-manager/resume' ``` The expected output is: ```bash {"status":"running"} ``` ### Check Procedure Manager Status Check Procedure Manager status by sending a GET request to the `/admin/procedure-manager/status` endpoint. ```bash curl -X GET 'http://localhost:4000/admin/procedure-manager/status' ``` The expected output is: ```bash {"status":"running"} ``` --- ## Cluster Recovery Mode Recovery mode is a safety feature in GreptimeDB that allows developers to manually recover the cluster from a failed state. ## When to Use Recovery Mode Recovery mode is particularly useful when the Datanode fails to start due to an "Empty region directory" error, often caused by: - Data corruption (Missing region data directory) - Recover the cluster from a metadata snapshot. ## Recovery Mode Management Recovery mode can be enabled and disabled through Metasrv's HTTP interface at: `http://{METASRV}:{HTTP_PORT}/admin/recovery/enable` and `http://{METASRV}:{HTTP_PORT}/admin/recovery/disable`. Note that this interface listens on Metasrv's `HTTP_PORT`, which defaults to `4000`. ### Enable Recovery Mode Enable recovery mode by sending a POST request to the `/admin/recovery/enable` endpoint. ```bash curl -X POST 'http://localhost:4000/admin/recovery/enable' ``` The expected output is: ```bash {"enabled":true} ``` ### Disable Recovery Mode Disable recovery mode by sending a POST request to the `/admin/recovery/disable` endpoint. ```bash curl -X POST 'http://localhost:4000/admin/recovery/disable' ``` The expected output is: ```bash {"enabled":false} ``` ### Check Recovery Mode Status Check recovery mode status by sending a GET request to the `/admin/recovery/status` endpoint. ```bash curl -X GET 'http://localhost:4000/admin/recovery/status' ``` The expected output is: ```bash {"enabled":false} ``` --- ## Resource ID Management Resource ID management is primarily used to manually set resource identifiers (such as table IDs) when restoring a cluster from a [Metadata Backup](/user-guide/deployments-administration/manage-metadata/restore-backup.md). This is because the backup file may not contain the latest `next table ID` value, which could lead to resource conflicts or data inconsistencies if not properly reset. ### Understanding the Relationship Between Table ID and Next Table ID In GreptimeDB: - **Table ID**: Each table has a unique numeric identifier used internally by the database to identify and manage tables - **Next Table ID**: The system-reserved next available table ID value. When creating a new table, the system automatically assigns this ID to the new table and then increments the `next table ID` **Example:** - Suppose the current cluster has tables with IDs 1001, 1002, and 1003 - The `next table ID` should be 1004 - When creating a new table, the system assigns ID 1004 to the new table and updates the `next table ID` to 1005 - If during backup restoration, the `next table ID` is still 1002, it would conflict with existing table IDs 1002 and 1003 (you would encounter the error `Region 1024 is corrupted, reason: ` when starting the Datanode) Under normal circumstances, resource IDs are automatically maintained by the database and require no manual intervention. However, in certain special scenarios (such as restoring a cluster from metadata backup where new tables were created after the backup), the `next table ID` in the backup may lag behind the actual cluster state, requiring manual adjustment. **How to determine if manual `next table ID` setting is needed:** 1. Query all table IDs in the current cluster: `SELECT TABLE_ID FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_ID DESC LIMIT 1;` 2. Get the current `next table ID` via API (see interface description below) 3. If the maximum existing table ID is greater than or equal to the current `next table ID`, you need to manually set the `next table ID` to a larger value, typically the maximum existing table ID plus 1. You can get or set the `next table ID` using Metasrv's HTTP interface at the following endpoints: `http://{METASRV}:{HTTP_PORT}/admin/sequence/table/next-id` (to get) and `http://{METASRV}:{HTTP_PORT}/admin/sequence/table/set-next-id` (to set). This interface listens on Metasrv's `HTTP_PORT`, which defaults to `4000`. ### Set next table ID To safely update the `next table ID`, follow this step-by-step process: 1. **Block new procedure submissions** - Pause the Procedure Manager. This rejects newly submitted procedures, including table creation. It does not suspend or cancel procedures that were already accepted, and their child procedures keep running. See [Prevent Metadata Changes](/user-guide/deployments-administration/maintenance/prevent-metadata-changes.md). 2. **Wait for in-flight procedures to finish** - Poll until no procedure is still in progress: ```sql SELECT procedure_id, procedure_type, status FROM INFORMATION_SCHEMA.PROCEDURE_INFO WHERE status IN ('Running', 'Retrying', 'PrepareRollback', 'RollingBack'); ``` Proceed only when this returns no rows. Terminal states such as `Done`, `Failed` and `Poisoned` are not included: their runners have already exited and released their locks, and the rows stay visible until background cleanup removes them. If procedures keep running past the window you allotted, stop here and investigate instead of changing the sequence. See [PROCEDURE_INFO](/reference/sql/information-schema/procedure-info.md). 3. **Enable cluster recovery mode** - Setting the `next table ID` is only allowed in recovery mode. See [Cluster Recovery Mode](/user-guide/deployments-administration/maintenance/recovery-mode.md) for more details. 4. **Set the next table ID** - Use the HTTP interface to set the `next table ID`. 5. **Restart metasrv nodes** - This ensures the new `next table ID` is properly applied. After the restart, confirm that recovery mode is still enabled and that the Procedure Manager is still paused, then verify the new `next table ID`. 6. **Disable cluster recovery mode** - Then resume the Procedure Manager to return to normal cluster operations. :::warning Recovery mode does not block DDL on its own. It only gates the `set-next-id` endpoint and relaxes Datanode startup checks. Pausing the Procedure Manager is what stops new metadata changes. ::: Set the `next table ID` by sending a POST request to the `/admin/sequence/table/set-next-id` endpoint: ```bash curl -X POST 'http://localhost:4000/admin/sequence/table/set-next-id' \ -H 'Content-Type: application/json' \ -d '{"next_table_id": 2048}' ``` The expected output is (the `next_table_id` may be different): ```bash {"next_table_id":2048} ``` ### Get next table ID ```bash curl -X GET 'http://localhost:4000/admin/sequence/table/next-id' ``` The expected output is (the `next_table_id` may be different): ```bash {"next_table_id":1254} ``` --- ## Table Reconciliation ## Overview GreptimeDB uses a layered metadata management architecture in distributed environments: - **Metasrv**: Acts as the metadata management layer, responsible for maintaining metadata information for all tables in the cluster - **Datanode**: Handles actual data storage and query execution, while also persisting some table metadata Under ideal conditions, table metadata between Metasrv and Datanode should remain perfectly consistent. However, in real production environments, restore from a [metadata backup](/user-guide/deployments-administration/manage-metadata/restore-backup.md) may cause metadata inconsistencies. **Table Reconciliation** is a table metadata repair mechanism provided by GreptimeDB that: - Detects metadata differences between Metasrv and Datanode - Repairs inconsistency issues according to predefined strategies - Ensures the system can recover from abnormal states to a consistent, available state ## Before starting Before starting the table reconciliation process, you need to: 1. Restore the cluster from a specific [metadata backup](/user-guide/deployments-administration/manage-metadata/restore-backup.md) 2. Set the [Next Table ID](/user-guide/deployments-administration/maintenance/sequence-management.md) to the original cluster's next table ID ## Repair Scenarios ### `Table not found` Error After a cluster is restored from specific metadata, write and query operations may encounter `Table not found` errors. - **Scenario 1**: The original cluster created new tables after the metadata backup, causing the new table metadata to not be included in the backup. This results in `Table not found` errors when querying these new tables. In this case, the new created table will be lost, and you must to manually set the [Next Table ID](/user-guide/deployments-administration/maintenance/sequence-management.md) to ensure that the restored cluster won't fail to create tables due to table ID conflicts when creating new tables. - **Scenario 2**: The original cluster renamed existing tables after the metadata backup. In this case, the new table names will be lost. ### `Empty region directory` Error After a cluster is restored from specific metadata, starting the Datanode may result in an `Empty region directory` error. This usually occurs because the original cluster deleted tables (executed `DROP TABLE`) after the metadata backup, causing the deleted table metadata to not be included in the backup, resulting in this error when starting the Datanode. For this situation, you need to enable [Recovery Mode](/user-guide/deployments-administration/maintenance/recovery-mode.md) after starting Metasrv when starting the cluster to ensure the Datanode can start normally. - **Mito Engine tables**: Table metadata cannot be repaired, you need to manually execute the `DROP TABLE` command to delete the non-existent table. - **Metric Engine tables**: Table metadata can be repaired, you need to manually execute the `ADMIN reconcile_table('table_name')` command to repair the table metadata. ### `No field named` Error After a cluster is restored from specific metadata, write and query operations may encounter `No field named` errors. This usually occurs because the original cluster deleted columns (executed `DROP COLUMN`) after the metadata backup, causing the deleted column metadata to not be included in the backup, resulting in this error when querying these deleted columns. For this situation, you need to manually execute the `ADMIN reconcile_table('table_name')` command to repair the table metadata. ### `schema has a different type` Error After a cluster is restored from specific metadata, write and query operations may encounter `schema has a different type` errors. This usually occurs because the original cluster modified column types (executed `MODIFY COLUMN [column_name] [type]`) after the metadata backup, causing the modified column type metadata to not be included in the backup, resulting in this error when querying these modified columns. For this situation, you need to manually execute the `ADMIN reconcile_table('table_name')` command to repair the table metadata. ### Missing Specific Columns After a cluster is restored from specific metadata, write and query operations may run normally but not include some columns. This occurs because the original cluster added columns (executed `ADD COLUMN`) after the metadata backup, causing the new column metadata to not be included in the backup, making these columns unavailable during queries. For this situation, you need to manually execute the `ADMIN reconcile_table('table_name')` command to repair the table metadata. ### Missing Column Indexes After a cluster is restored from specific metadata, write and query operations may run normally, but `SHOW CREATE TABLE`/`SHOW INDEX FROM [table_name]` shows that certain columns don't include expected indexes. This occurs because the original cluster modified indexes (executed `MODIFY INDEX [column_name] SET [index_type] INDEX`) after the metadata backup, causing the index change metadata to not be included in the backup. For this situation, you need to manually execute the `ADMIN reconcile_table('table_name')` command to repair the table metadata. ## Repair operations GreptimeDB provides the following Admin functions to trigger table metadata repair: ### Repair All Tables Repair the metadata inconsistency of all tables in the entire cluster: ```sql ADMIN reconcile_catalog() ``` ### Repair a Specific Database Repair the metadata inconsistency of all tables in a specific database: ```sql ADMIN reconcile_database('database_name') ``` ### Repair a Specific Table Repair the metadata inconsistency of a single table: ```sql ADMIN reconcile_table('table_name') ``` ### View Repair Progress After the Admin function is executed, it will return a `ProcedureID`, you can use the following command to view the progress of the repair task: ```sql ADMIN procedure_state('procedure_id') ``` When `procedure_state` returns Done, it indicates that the repair task has completed. ## Important Notes When performing table metadata repair operations, please note the following: - Repair operations are executed asynchronously, and you can check the execution progress through the `procedure_id` - It is recommended to perform repair operations during low-traffic periods to reduce the impact on system performance - For large-scale repair operations (such as `reconcile_catalog()`), it is recommended to validate in a test environment first --- ## Basic Table Operations [Data Model](/user-guide/concepts/data-model.md) should be read before this guide. GreptimeDB provides table management functionalities via SQL. The following guide uses [MySQL Command-Line Client](https://dev.mysql.com/doc/refman/8.0/en/mysql.html) to demonstrate it. For more explanations of the `SQL` syntax, please see the [SQL reference](/reference/sql/overview.md). ## Create a database The default database is `public`. You can create a database manually. ```sql CREATE DATABASE test; ``` ```sql Query OK, 1 row affected (0.05 sec) ``` Create a database with a `TTL` (Time-To-Live) of seven days, which means all the tables in this database will inherit this option if they don't have their own `TTL` setting: ```sql CREATE DATABASE test with(ttl='7d'); ``` You can list all the existing databases. ```sql SHOW DATABASES; ``` ```sql +--------------------+ | Database | +--------------------+ | greptime_private | | information_schema | | public | | test | +--------------------+ 4 rows in set (0.00 sec) ``` Using `like` syntax: ```sql SHOW DATABASES LIKE 'p%'; ``` ```sql +----------+ | Database | +----------+ | public | +----------+ 1 row in set (0.00 sec) ``` Then change the database: ```sql USE test; ``` ## Create a table :::tip NOTE GreptimeDB offers a schemaless approach to writing data that eliminates the need to manually create tables using additional protocols. See [Automatic Schema Generation](/user-guide/ingest-data/overview.md#automatic-schema-generation). ::: You can still create a table manually via SQL if you have specific requirements. Suppose we want to create a table named monitor with the following data model: - `host` is the hostname of the collected standalone machine, which should be a `Tag` that used to filter data when querying. - `ts` is the time when the data is collected, which should be the `Timestamp`. It can also used as a filter when querying data with a time range. - `cpu` and `memory` are the CPU utilization and memory utilization of the machine, which should be `Field` columns that contain the actual data and are not indexed. The SQL code for creating the table is shown below. In SQL, we use the primary key to specify `Tag`s and the `TIME INDEX` to specify the `Timestamp` column. The remaining columns are `Field`s. ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64 DEFAULT 0, memory FLOAT64, PRIMARY KEY(host)); ``` ```sql Query OK, 0 row affected (0.03 sec) ``` Create the table with a `TTL` (Time-To-Live) of seven days: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64 DEFAULT 0, memory FLOAT64, PRIMARY KEY(host) ) WITH (ttl='7d'); ``` :::warning NOTE GreptimeDB does not currently support changing the TIME INDEX after a table has been created. Therefore, it is important to carefully choose your TIME INDEX column before creating tables. ::: ### `CREATE TABLE` syntax - Time index: Every GreptimeDB table requires exactly one time index. Declare it with the `TIME INDEX` keyword. The column must use a `TIMESTAMP` data type. - Primary key: The columns in primary key are similar to tags in other other time-series systems like [InfluxDB][1]. The primary key columns with the time index column are used to uniquely define a series of data, which is similar to time series like [InfluxDB][2]. - Table options: when creating a table, you can specify a set of table options, click [here](/reference/sql/create.md#table-options) for more details. ### Table name constraints GreptimeDB supports a limited set of special characters in table names, but they must adhere to the following constraints: - A valid GreptimeDB table name must start with a letter (either lowercase or uppercase) or `-` / `_` / `:`. - The rest part of table name can be alphanumeric or special characters within: `-` / `_` / `:` / `@` / `#`. - Any table name containing special characters must be quoted with backquotes. - Any table name containing uppercase letters must be quoted with backquotes. Here are some examples: ```sql -- ✅ Ok create table a (ts timestamp time index); -- ✅ Ok create table a0 (ts timestamp time index); -- 🚫 Invalid table name create table 0a (ts timestamp time index); -- 🚫 Invalid table name create table -a (ts timestamp time index); -- ✅ Ok create table `-a` (ts timestamp time index); -- ✅ Ok create table `a@b` (ts timestamp time index); -- 🚫 Invalid table name create table memory_HugePages (ts timestamp time index); -- ✅ Ok create table `memory_HugePages` (ts timestamp time index); ``` [1]: https://docs.influxdata.com/influxdb/v1.8/concepts/glossary/#tag-key [2]: https://docs.influxdata.com/influxdb/v1/concepts/glossary/#series ## Describe a table Show table information in detail: ```sql DESC TABLE monitor; ``` ```sql +--------+----------------------+------+------+---------------------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------+----------------------+------+------+---------------------+---------------+ | host | String | PRI | YES | | TAG | | ts | TimestampMillisecond | PRI | NO | current_timestamp() | TIMESTAMP | | cpu | Float64 | | YES | 0 | FIELD | | memory | Float64 | | YES | | FIELD | +--------+----------------------+------+------+---------------------+---------------+ 4 rows in set (0.01 sec) ``` The Semantic Type column describes the data model of the table. The `host` is a `Tag` column, `ts` is a `Timestamp` column, and cpu and memory are `Field` columns. ## Show Table Definition and Indexes Use `SHOW CREATE TABLE table_name` to get the statement when creating the table: ```sql SHOW CREATE TABLE monitor; ``` ``` +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | monitor | CREATE TABLE IF NOT EXISTS `monitor` ( `host` STRING NULL, `ts` TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(), `cpu` DOUBLE NULL DEFAULT 0, `memory` DOUBLE NULL, TIME INDEX (`ts`), PRIMARY KEY (`host`) ) ENGINE=mito | +---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ``` List all indexes in a table: ```sql SHOW INDEXES FROM monitor; ``` ```sql +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+-------------------------+---------+---------------+---------+------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+-------------------------+---------+---------------+---------+------------+ | monitor | 1 | PRIMARY | 1 | host | A | NULL | NULL | NULL | YES | greptime-primary-key-v1 | | | YES | NULL | | monitor | 1 | TIME INDEX | 1 | ts | A | NULL | NULL | NULL | NO | | | | YES | NULL | +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+-------------------------+---------+---------------+---------+------------+ ``` For more info about `SHOW` statement, please read the [SHOW reference](/reference/sql/show.md). ## List Existing Tables You can use `show tables` statement to list existing tables ```sql SHOW TABLES; ``` ```sql +----------------+ | Tables_in_test | +----------------+ | monitor | +----------------+ 1 row in set (0.00 sec) ``` Currently only table name filtering is supported. You can filter existing tables by their names. ```sql SHOW TABLES LIKE monitor; ``` ```sql +----------------+ | Tables_in_test | +----------------+ | monitor | +----------------+ 1 row in set (0.00 sec) ``` List tables in a specific database: ```sql SHOW TABLES FROM test; ``` ```sql +----------------+ | Tables_in_test | +----------------+ | monitor | +----------------+ 1 row in set (0.01 sec) ``` ## Alter a table You can alter the schema of existing tables just like in MySQL database ```sql ALTER TABLE monitor ADD COLUMN label VARCHAR; ``` ```sql Query OK, 0 rows affected (0.03 sec) ``` ```sql ALTER TABLE monitor DROP COLUMN label; ``` ```sql Query OK, 0 rows affected (0.03 sec) ``` The `ALTER TABLE` statement also supports adding, removing, and renaming columns, as well as modifying the column datatype, etc. Please refer to the [ALTER reference](/reference/sql/alter.md) for more information. ## Drop a table :::danger danger Without [soft-drop](/enterprise/soft-drop.md) (GreptimeDB Enterprise only), `DROP TABLE` cannot be undone. Use it with care! ::: `DROP TABLE [db.]table` is used to drop the table in `db` or the current database in-use.Drop the table `monitor` in the current database: ```sql DROP TABLE monitor; ``` ```sql Query OK, 1 row affected (0.01 sec) ``` ## Drop a database :::danger danger `DROP DATABASE` cannot be undone. Use it with care! ::: You can use `DROP DATABASE` to drop a database. For example, change back to `public` database and drop the `test` database: ```sql USE public; DROP DATABASE test; ``` Please refer to the [DROP](/reference/sql/drop.md#drop-database) document for more details. ## HTTP API You can execute the SQL statements through the HTTP API. For example, using the following code to create a table through POST method: ```shell curl -X POST \ -H 'authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=CREATE TABLE monitor (host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), cpu FLOAT64 DEFAULT 0, memory FLOAT64, TIME INDEX (ts), PRIMARY KEY(host))' \ http://localhost:4000/v1/sql?db=public ``` ```json { "output": [{ "affectedrows": 0 }], "execution_time_ms": 10 } ``` For more information about SQL HTTP request, please refer to [API document](/user-guide/protocols/http.md#post-sql-statements). ## Time zone The specified time zone in the SQL client session will affect the default timestamp value when creating or altering a table. If you set the default value of a timestamp column to a string without a time zone, the client's time zone information will be automatically added. For more information about the effect of the client time zone, please refer to the [time zone](/user-guide/ingest-data/for-iot/sql.md#time-zone) section in the write data document. --- ## Compaction For databases based on the LSM Tree, compaction is extremely critical. It merges overlapping fragmented SST files into a single ordered file, discards deleted data while significantly improves query performance. Since v0.9.1, GreptimeDB provides compaction strategies to control how SST files are compacted: Time Windowed Compaction Strategy (TWCS) and Strict Window Compaction Strategy (SWCS). ## Concepts Let us start with those core concepts of compaction in GreptimeDB. ### SST files SSTs are sorted files generated when memtables are flushed to persistent storage, such as disks and object stores. In GreptimeDB, data rows in SST files are organized by [tags](/user-guide/concepts/data-model.md) and timestamps, as illustrated below. Each SST file covers a specific time range. When a query specifies a time range, GreptimeDB retrieves only the relevant SST files that may contain data within that range, rather than loading all persisted files. ![SST layout](/compaction-sst-file-layout.jpg) Typically, the time ranges of SST files do not overlap during real-time writing workloads. However, due to factors like row deletions and out-of-order writes, SST files may have overlapping time ranges, which can affect query performance. ### Time window Time-series workloads present prominent "windowed" pattern in that most recently inserted rows are more possible to be read. Thus GreptimeDB logically partitioned the time axis into different time windows and we are more interested in compacting those SST files fall into the same time window. Time window for some specific table is usually inferred from the most-recently flushed SST files or users may manually specify the time window if TWCS is chosen. GreptimeDB has a preset collection of window sizes that are: - 1 hour - 2 hours - 12 hours - 1 day - 1 week If time window is not specified, GreptimeDB will use `1hour` as the initial time window size and split all inserted rows to those time windows and will infer a proper time window size when the first compaction happens by selecting the minimum time window size for the above collection that can cover the whole time span of all files to be compacted. For example, during the first compaction, the time span for all SST files is 4 hours, then GreptimeDB will choose 12 hours as the time window for that table and persist it for later compactions. ### Sorted runs Sorted runs is a collection of SST files that have sorted and non-overlapping time ranges. For example, a table contains 5 SSTs with following time ranges (all inclusive): `[0, 10]`, `[12, 23]`, `[22, 25]`,`[24, 30]`,`[26,33]` and we can find 2 sorted runs: ![num-of-sorted-runs](/compaction-num-sorted-runs.jpg) The number of sorted runs indicates the orderliness of SST files. More sorted runs typically lead to worse query performance. The main goal of compactions is to reduce the number of sorted runs. ### Levels Databases based on LSM trees may also have levels, keys are merged level by level. GreptimeDB only has 2 levels which are 0 (uncompacted) and 1 (compacted). ## Compaction strategies GreptimeDB provides two compaction strategies as mentioned above, but only Time Windowed Compaction Strategy (TWCS) can be chosen when creating tables. Strict Window Compaction Strategy (SWCS) is only available when executing manual compactions. ## Time Windowed Compaction Strategy (TWCS) TWCS primarily aims to reduce read/write amplification during compaction. It assigns files to be compacted into different time windows. For each window, TWCS identifies the sorted runs. If there are more than 1 sorted runs, TWCS finds a solution to merge the overlapping files in those runs with merging penalties taken into consideration. If there's only 1 sorted run, TWCS checks for excessive file fragmentation and merges fragmented files if necessary since SST file count also impacts query performance. For window assignment, SST files may span multiple time windows. TWCS assigns SSTs based on their maximum timestamps to ensure they are not affected by stale data. In time-series workloads, out-of-order writes are infrequent, and even when they occur, recent data's query performance is more critical than that of stale data. Common TWCS table options include: - `trigger_file_num`: number of files in a specific time window to trigger a compaction (default 4). - `time_window`: time window size for TWCS compaction. - `max_output_file_size`: max allowed compaction output file size (default 512MB). Following diagrams show how files in a window get compacted when `trigger_file_num = 3`: - In A, there're two SST files `[0, 3]` and `[5, 6, 9]` but there's only one sorted run since those two files have disjoint time ranges. - In B, a new SST file `[1, 4]` is flushed therefore forms two sorted runs that exceeds the threshold. Then `[0, 3]` and `[1, 4]` are merged to `[0, 1, 3, 4]`. - In C, a new SST file `[9, 10]` is flushed, and it will be merged with `[5, 6, 10]` to create `[5, 6, 9, 10]`, and after compaction the files will be like D. - In E, a new file `[11, 12]` is flushed. Though there's still only one sorted run, but the number of files reaches `trigger_file_num`(3), so `[5, 6, 9, 10]` will be merged with `[11, 12]` to form `[5, 6, 9, 10, 11, 12]`. ![compaction-trigger-file-num.png](/compaction-trigger-file-num.png) ### Specifying TWCS parameters TWCS parameters can be specified at two levels: 1. **Table level**: Explicitly set when creating or altering a table 2. **Database level**: Set as defaults for all tables in the database The effective compaction settings are resolved dynamically at compaction scheduling time with the following priority: - Table-level settings (if specified) - Database-level settings (if specified and no table-level override) - Built-in defaults #### Table-level compaction settings You can specify TWCS parameters when creating tables: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64 DEFAULT 0, memory FLOAT64, PRIMARY KEY(host)) WITH ( 'compaction.type'='twcs', 'compaction.twcs.trigger_file_num'='8', 'compaction.twcs.time_window'='1h', 'compaction.twcs.max_output_file_size'='500MB' ); ``` #### Database-level compaction settings You can also set default compaction parameters for all tables in a database. These settings will be inherited by tables that don't have explicit compaction settings. See [ALTER DATABASE](/reference/sql/alter.md#modify-compaction-options-of-database) for examples. :::note Unlike table-level settings which are stored with the table metadata, database-level compaction settings are dynamically resolved at runtime. Changing database-level compaction options will immediately affect all tables in the database that don't have their own explicit compaction settings. This behavior is similar to how [database-level TTL](/reference/sql/create.md#create-database) works. ::: ## Strict Window Compaction Strategy (SWCS) and manual compaction Unlike TWCS, which assigns one window per SST file based on their maximum timestamps, SWCS assigns SST files to **all** overlapping windows. Consequently, a single SST file may be included in multiple compaction outputs, as its name suggests. Due to its high read amplification during compaction, SWCS is not the default compaction strategy. However, it is useful when you need to manually trigger compaction to reorganize the layout of SST files—especially if an individual SST file spans a large time range that significantly slows down queries. GreptimeDB offers a simple SQL function for triggering compaction: ```sql ADMIN COMPACT_TABLE( , , [] ); ``` The `` parameter can be `regular` (or `twcs`) for regular TWCS compaction, or `swcs` (or `strict_window`) for Strict Window Compaction Strategy. The value is case-insensitive. For the `swcs` strategy, the `` can specify: - The window size (in seconds) for splitting SST files - The `parallelism` parameter to control the level of parallelism for compaction (defaults to 1) For example, to trigger compaction with a 1-hour window: ```sql ADMIN COMPACT_TABLE( "monitor", "swcs", "3600" ); +------------------------------------------------+ | ADMIN COMPACT_TABLE("monitor", "swcs", "3600") | +------------------------------------------------+ | 0 | +------------------------------------------------+ 1 row in set (0.01 sec) ``` When executing this statement, GreptimeDB will split each SST file into segments with a time span of 1 hour (3600 seconds) and merge these segments into a single output, ensuring no overlapping files remain. You can also specify the `parallelism` parameter to speed up compaction by processing multiple files concurrently: ```sql -- SWCS compaction with default time window and parallelism set to 2 ADMIN COMPACT_TABLE("monitor", "swcs", "parallelism=2"); -- SWCS compaction with custom time window and parallelism ADMIN COMPACT_TABLE("monitor", "swcs", "window=1800,parallelism=2"); ``` The `parallelism` parameter is also available for regular compaction: ```sql -- Regular compaction with parallelism set to 2 ADMIN COMPACT_TABLE("monitor", "regular", "parallelism=2"); ``` ### Limit manual compaction to a time range Both regular and SWCS manual compactions can be limited to a time range by setting `start_time` and `end_time` in the options string: ```sql -- Regular compaction for a time range ADMIN COMPACT_TABLE( "monitor", "regular", "parallelism=2,start_time=2026-01-01T00:00:00Z,end_time=2026-02-01T00:00:00Z" ); -- SWCS compaction for a time range ADMIN COMPACT_TABLE( "monitor", "strict_window", "window=3600,start_time=2026-01-01T00:00:00Z,end_time=2026-02-01T00:00:00Z" ); ``` The time range has the following rules and behavior: - `start_time` and `end_time` must be specified together, and `start_time` must be earlier than `end_time`. - The range is half-open: `[start_time, end_time)`. It selects compaction windows that overlap the range rather than filtering individual rows. - A timestamp with an explicit time zone or UTC offset uses that zone. A timestamp without one uses the current query session time zone. - Regular compaction keeps the existing TWCS trigger rules and only considers candidate windows that overlap the range. Specifying a range does not force an otherwise ineligible window to be compacted. - SWCS preserves its forced-compaction behavior. If an SST file in a selected window also spans other windows, GreptimeDB transitively includes those dependent windows. The actual compacted windows can therefore extend beyond the requested range; this ensures that all rows from shared input SST files are retained. The following diagram shows the process of strict window compaction: In Figure A, there are 3 overlapping SST files: `[0, 3]` (which includes timestamps 0, 1, 2, and 3), `[3, 8]`, and `[8, 10]`. The strict window compaction strategy will assign the file `[3, 8]` that covers windows 0, 4, and 8 to three separate windows respectively. This allows it to merge with `[0, 3]` and `[8, 10]` separately. Figure B shows the final compaction result with three files: `[0, 3]`, `[4, 7]`, and `[8, 10]`. These files do not overlap with each other. ![compaction-strict-window.jpg](/compaction-strict-window.jpg) --- ## Garbage Collection (GC) GreptimeDB GC delays physical deletion of SST/index files until all references (running queries, [repartition](./repartition.md) cross-region file refs) are released. The configuration contains two parts: - Metasrv Configuration - Datanode Configuration ## How it works - **Roles**: Meta decides when/where to clean; datanodes perform the actual delete while keeping in-use files safe. - **Safety windows**: `lingering_time` holds known-removed files a bit longer; `unknown_file_lingering_time` is a rare-case guard that only applies during full listing GC. - **Listing modes**: Fast mode removes files the system already marked; full listing walks storage to catch stragglers/orphans. ![GC workflow](/gc-flow.svg) ## Metasrv Configuration On the Metasrv side, GC schedules cleanup tasks for regions and coordinates when to run GC. ```toml [gc] enable = true # Enable meta GC scheduler. default to be false; must match datanode. gc_cooldown_period = "5m" # Minimum gap before the same region is GCed again. [gc.experimental_soft_drop] enable = false # Enable soft-drop tables (GreptimeDB Enterprise only). Requires gc.enable = true. retention = "7d" # How long soft-dropped tables are retained before purge. ``` ### Options | Configuration Option | Description | | --- | --- | | `enable` | Enable the meta GC scheduler. Must match datanode GC enablement. | | `gc_cooldown_period` | Minimum interval before the same region is scheduled for GC again; keep datanode `lingering_time` longer than this. | | `experimental_soft_drop.enable` | Enable [soft-drop tables](/enterprise/soft-drop.md) (GreptimeDB Enterprise only). Requires `gc.enable = true`. | | `experimental_soft_drop.retention` | How long soft-dropped tables are retained before automatic purge. | ## Datanode Configuration The Datanode side performs the actual deletion while protecting files still in use. ```toml [[region_engine]] [region_engine.mito] [region_engine.mito.gc] enable = true # Turn on datanode GC worker; must match meta. lingering_time = "10m" # Keep known-removed files this long for active queries. unknown_file_lingering_time = "1d" # Keep files without expel time; rare safeguard. ``` ### Options | Configuration Option | Description | | --- | --- | | `enable` | Enable the datanode GC worker. Must match meta GC `enable`. | | `lingering_time` | How long to keep manifest-removed files before deletion to protect long follower-region queries/cross-region references; set longer than `gc_cooldown_period`. Use `"0s"` to delete immediately. | | `unknown_file_lingering_time` | Safety hold for files without expel time (not tracked in manifest). Applies only during full listing GC. Defaults to `1d`. | For an active or open region, an unknown file is deleted only once its object-store last-modified timestamp is older than `now - unknown_file_lingering_time`. If the object store does not report a last-modified timestamp, the file is kept. Unknown files belonging to dropped regions are deleted immediately, without waiting for this window. :::warning Keep `unknown_file_lingering_time` generous. A long-running flush or compaction writes files before they appear in the manifest, so a short window can delete data that is still being written. ::: :::warning `gc.enable` must be set consistently on metasrv and all datanodes. Mismatched flags cause GC to be skipped or stuck. ::: ## When to enable - GC only applies when tables use object storage; tables on local filesystems ignore GC settings. - Turn on GC if need to repartition so cross-region references can drain safely before deletion. - For clusters with long-running follower-region queries, turn on GC and set `lingering_time` longer than `gc_cooldown_period` so files created or referenced during a GC cycle stay alive (in-use or lingering) until at least the next cycle. - Leave GC off if you are not repartitioning and do not need delayed deletion. ## Operational notes - GC is designed for object storage backends (with list/delete support); ensure your store credentials and permissions allow listing and deletion. - Deleted files live in object storage until GC removes them; ensure storage listing/deletion permissions are in place. - After enabling, restart metasrv and datanodes to apply config changes. --- ## Table and Region Operations --- ## Region Failover Region Failover provides the ability to recover regions from region failures. With Kafka WAL (Remote WAL) and shared storage, Region Failover can recover regions without losing data. This is implemented via [Region Migration](/user-guide/deployments-administration/manage-data/region-migration.md). ## Enable the Region Failover This feature is only available on GreptimeDB running on distributed mode and - Using Kafka WAL (Remote WAL) or Local WAL with `allow_region_failover_on_local_wal=true` (enabling region failover on Local WAL may lead to data loss during failover) - Using [shared storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3) If you want to enable region failover on local WAL, you need to set `allow_region_failover_on_local_wal=true` in [metasrv](/user-guide/deployments-administration/configuration.md#metasrv-only-configuration) configuration file. It's not recommended to enable this option, because it may lead to data loss. ### Via configuration file Set `enable_region_failover=true` in the [metasrv](/user-guide/deployments-administration/configuration.md#metasrv-only-configuration) configuration file. Additionally, set `region_failure_detector_initialization_delay` to a larger value and enable [Cluster Maintenance Mode](/user-guide/deployments-administration/maintenance/maintenance-mode.md) within the `region_failure_detector_initialization_delay` period to avoid triggering unnecessary Region Failover during datanode startup or upgrades. ### Via GreptimeDB Operator To enable region failover via GreptimeDB Operator, you can refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#enable-region-failover) for more details. ## The recovery time of Region Failover The recovery time of Region Failover with Kafka WAL (Remote WAL) depends on: - number of regions per Topic. - the Kafka cluster read throughput performance. ### The read amplification In best practices, [the number of topics/partitions supported by a Kafka cluster is limited](https://docs.aws.amazon.com/msk/latest/developerguide/bestpractices.html) (exceeding this number can degrade Kafka cluster performance). Therefore, we allow multiple regions to share a single topic as the WAL. However, this may cause to the read amplification issue. The data belonging to a specific region consists of data files plus data in the WAL (typically `WAL[LastCheckpoint...Latest]`). The failover of a specific region only requires reading the region's WAL data to reconstruct the memory state, which is called region replaying. However, If multiple regions share a single topic, replaying data for a specific region from the topic requires filtering out unrelated data (i.e., data from other regions). This means replaying data for a specific region from the topic requires reading more data than the actual size of the region's data in the topic, a phenomenon known as read amplification. Although multiple regions share the same topic, allowing the Datanode to support more regions, the cost of this approach is read amplification during WAL replay. For example, configure 128 topics for [metasrv](/user-guide/deployments-administration/configuration.md#metasrv-only-configuration), and if the whole cluster holds 1024 regions (physical regions), every 8 regions will share one topic. ![Read Amplification](/remote-wal-read-amplification.png) (Figure1: recovery Region 3 need to read redundant data 7 times larger than the actual size) A simple model to estimate the redundant read amplification factor (redundant replay data size/actual data size): - For a single topic, if we try to replay all regions that belong to the topic, then the amplification factor would be 7+6+...+1 = 28 times. (The Region WAL data distribution is shown in the Figure 1. Replaying Region 3 will read 7 times redundant data larger than the actual size; Region 6 will read 6 times, and so on) - When recovering 100 regions (requiring about 13 topics), the amplification factor is approximately 28 \* 13 = 364 times. Assuming we have 100 regions to recover, and the actual data size of all regions is 0.5GB, the following table shows the replay data size based on the number of regions per topic. | Number of regions per Topic | Number of topics required for 100 Regions | Single topic redundant read factor | Total redundant read factor | Replay data size (GB) | | --------------------------- | ----------------------------------------- | -------------------------------------- | ---------------------------------- | ---------------- | | 1 | 100 | 0 | 0 | 0.5 | | 2 | 50 | 1 | 50 | 25.5 | | 4 | 25 | 6 | 150 | 75.5 | | 8 | 13 | 28 | 364 | 182.5 | | 16 | 7 | 120 | 840 | 420.5 | The following table shows the recovery time of 100 regions under different read throughput conditions of the Kafka cluster. For example, when providing a read throughput of 300MB/s, recovering 100 regions requires approximately 10 minutes (182.5GB/0.3GB = 10m). | Number of regions per Topic | Replay data size (GB) | Kafka throughput 300MB/s- Recovery time (secs) | Kafka throughput 1000MB/s- Recovery time (secs) | | --------------------------- | ---------------- | --------------------------------------------- | ---------------------------------------------- | | 1 | 0.5 | 2 | 1 | | 2 | 25.5 | 85 | 26 | | 4 | 75.5 | 252 | 76 | | 8 | 182.5 | 608 | 183 | | 16 | 420.5 | 1402 | 421 | ### Suggestions for improving recovery time In the above example, we calculated the recovery time based on the number of Regions contained in each Topic for reference. We have calculated the recovery time under different Number of regions per Topic configuration for reference. In actual scenarios, the read amplification may be larger than this model. If you use Kafka WAL and are very sensitive to recovery time, we recommend that each region have its own topic (i.e., Number of regions per Topic is 1). --- ## Region Migration Region Migration allows users to move regions between the Datanode. :::warning Warning This feature is only available on GreptimeDB running on distributed mode and - Using [shared storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3) Otherwise, you can't perform a region migration. ::: ## Figure out the region distribution of the table. You need to first query the region distribution of the table, i.e., find out on which Datanode the Regions in the table are located. ```sql select b.peer_id as datanode_id, a.greptime_partition_id as region_id from information_schema.partitions a left join information_schema.region_peers b on a.greptime_partition_id = b.region_id where a.table_name='migration_target' order by datanode_id asc; ``` For example, have the following region distribution: ```sql +-------------+---------------+ | datanode_id | region_id | +-------------+---------------+ | 1 | 4398046511104 | +-------------+---------------+ 1 row in set (0.01 sec) ``` For more info about the `region_peers` table, please read the [REGION-PEERS](/reference/sql/information-schema/region-peers.md). ## Select a Datanode as the migration destination. :::warning Warning The region migration won't be performed if the `from_peer_id` equals the `to_peer_id`. ::: Remember, if you deploy the cluster via the GreptimeDB operator, the `peer_id` of Datanode always starts from 0. For example, if you have a 3 Datanode GreptimeDB cluster, the available `peer_id` will be 0,1,2. Finally, you can do a Region migration request via the following SQL: ```sql ADMIN migrate_region(4398046511104, 1, 2, 60); ``` The parameters of `migrate_region`: ```sql ADMIN migrate_region(region_id, from_peer_id, to_peer_id); ADMIN migrate_region(region_id, from_peer_id, to_peer_id, replay_timeout); ``` | Option | Description | Required | | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | --- | | `region_id` | The region id. | **Required** | | | `from_peer_id` | The peer id of the migration source(Datanode). | **Required** | | | `to_peer_id` | The peer id of the migration destination(Datanode). | **Required** | | | `replay_timeout` | The timeout (in seconds) for replaying data. Defaults to 300 seconds if omitted. If the new Region fails to replay the data within the specified timeout, the migration will fail, however the data in the old Region will not be lost. | Optional | | ## Query the migration state The `migrate_region` function returns the procedure id that executes the migration, queries the procedure state by it: ```sql ADMIN procedure_state('538b7476-9f79-4e50-aa9c-b1de90710839') ``` If it's done, outputs the state in JSON: ```json {"status":"Done"} ``` Of course, you can confirm the region distribution by querying from `region_peers` and `partitions` in `information_schema`. --- ## Repartition Repartition lets you adjust partition rules after a table has been created. GreptimeDB does this through `ALTER TABLE` partition, split, and merge operations; see [ALTER TABLE](/reference/sql/alter.md#split-or-merge-partitions) for the syntax. Repartition is only supported in distributed clusters. ## How it works The core idea is to adjust partition rules and Region routing online instead of manually moving data into a new table. GreptimeDB switches to the new partition layout by updating manifest file references for each Region, so the rules can better match the current data distribution. This approach is useful when traffic patterns change over time and you want to keep partition rules aligned with the workload without rebuilding the table. During repartitioning, writes may be briefly affected, so client-side retries are recommended. ## When to repartition Consider repartitioning when: - Some regions are consistently hotter than others. - Your data distribution changes and the current partition boundaries no longer fit. - Some regions become very small and cold, and you want to reduce resource usage by merging them. - You want to further split a partition to improve write concurrency or query performance. In general, when partition rules no longer reflect the current data distribution well, it is worth considering repartitioning. ## How to identify hot partitions Before repartitioning, confirm which regions are hot. Join region statistics with partition metadata to find the hottest rules: ```sql SELECT t.table_name, r.region_id, r.region_number, p.partition_name, p.partition_description, r.region_role, r.written_bytes_since_open, r.region_rows FROM information_schema.region_statistics r JOIN information_schema.tables t ON r.table_id = t.table_id JOIN information_schema.partitions p ON p.table_schema = t.table_schema AND p.table_name = t.table_name AND p.greptime_partition_id = r.region_id WHERE t.table_schema = 'public' AND t.table_name = 'your_table' ORDER BY r.written_bytes_since_open DESC LIMIT 10; ``` If some regions have a much higher `written_bytes_since_open` value over time, that partition rule is usually a good candidate for splitting. Also check whether the region peers are healthy so node issues are not mistaken for hotspots: ```sql SELECT p.region_id, p.peer_addr, p.status, p.down_seconds FROM information_schema.region_peers p WHERE p.table_schema = 'public' AND p.table_name = 'your_table' ORDER BY p.region_id, p.peer_addr; ``` If the nodes are healthy and the hotspot signal persists, you can move on to designing the repartition plan. ## Prerequisites :::warning Warning This feature is only available in distributed clusters and requires: - Using [shared object storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3) - Using [GC](/user-guide/deployments-administration/manage-data/gc.md) on both metasrv and all datanodes Otherwise, you can't perform repartitioning. ::: GreptimeDB supports repartitioning an unpartitioned table with `PARTITION ON COLUMNS`, and adjusting an existing partitioned table through repeated `SPLIT PARTITION` and `MERGE PARTITION` operations. The most common follow-up changes are 1-to-2 splits and 2-to-1 merges. More complex changes can also be done step by step. Object storage stores region files, while GC reclaims old files only after references are released. This helps prevent accidental deletion of data still in use. ### Through GreptimeDB Operator If you deploy GreptimeDB with the GreptimeDB Operator, refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) for GC and object storage setup. ## Example You can first partition an unpartitioned table, and later keep tuning the layout by merging existing partitions and splitting them with new rules. Partition an unpartitioned table: ```sql ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id, area) ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South', device_id >= 100 AND area <= 'East', device_id >= 100 AND area > 'East' ); ``` Adjust an existing partitioned table. The following example changes the `area` partition key from `South` to `North` for devices with `device_id < 100`: ```sql ALTER TABLE sensor_readings MERGE PARTITION ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ); ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 ) INTO ( device_id < 100 AND area < 'North', device_id < 100 AND area >= 'North' ); ``` To introduce a new partition column, use `SPLIT PARTITION ... ON COLUMNS`. The following example starts with a table partitioned only by `device_id`, then introduces `area` as a new partition column: ```sql ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id) ( device_id < 100, device_id >= 100 ); ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 ) ON COLUMNS (device_id, area) INTO ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ); ``` After the split, the table's partition columns are updated to `(device_id, area)`. ## Further reading For a step-by-step tutorial with more background and examples, see [How to Split and Merge Partitions Online in GreptimeDB](https://greptime.com/blogs/2026-03-19-greptimedb-repartition-guide). --- ## Table Sharding Table sharding is a technique used to distribute a large table into multiple smaller tables. This practice is commonly employed to enhance the performance of database systems. In GreptimeDB, data is logically sharded into partitions. Since GreptimeDB uses "tables" to group data and SQL to query them, we adopt the term "partition," a concept frequently used in OLTP databases. ## When to shard a table Inside GreptimeDB, both data management and scheduling are based on the region level, and each region is corresponding to a table partition. Thus when you have a table that is too large to fit into a single node, or the table is too hot to be served by a single node, you should consider sharding it. A region in GreptimeDB has a relative fixed throughput capacity, and the number of regions in a table determines the total throughput capacity of the table. If you want to increase the throughput capacity of a table, you can increase the number of regions in the table. Ideally the overall throughput of a table should be proportional to the number of regions. As for which specific partition column to use or how many regions to create, it depends on the data distribution and the query pattern. A general goal is to make the data distribution among regions as even as possible. And the query pattern should be considered when designing the partition rule set as one query can be processed in parallel among regions. In other word the query latency is depends on the "slowest" region's latency. But notice that the increase of regions will bring some basic consumption and increase the complexity of the system. You need to consider the requirement of data ingest rate, the query performance, the data distribution on storage system. You should shard a table only when necessary. For more information on the relationship between partitions and regions, refer to the [Table Sharding](/contributor-guide/frontend/table-sharding.md) section in the contributor guide. ## Partition In GreptimeDB, a table can be horizontally partitioned by column value ranges. Currently, GreptimeDB supports range partitioning with the `PARTITION ON COLUMNS` syntax. Each partition includes only a portion of the data from the table, and is grouped by some column(s) value range. For example, we can partition a table in GreptimeDB like this: ```sql CREATE TABLE (...) PARTITION ON COLUMNS () ( ); ``` The syntax mainly consists of two parts: 1. `PARTITION ON COLUMNS` followed by a comma-separated list of column names. This specifies which columns will be used for partitioning. Currently, any column defined in the table can be used as a partition column. Note that the ranges of all partitions must **not** overlap. 2. `RULE LIST` is a list of multiple partition rules. Each rule is a partition condition, and GreptimeDB generates partition names such as `p0`, `p1`, and so on for these rules. The expressions here can use `=`, `!=`, `>`, `>=`, `<`, `<=`, `AND`, `OR`, column names, and literals. :::tip Choosing partition columns We recommend partitioning on Tag columns (the primary key columns). This matters most for tables that are **not** append-only (deduplication is enabled): the partition columns should be a subset of the primary key columns. That way, rows with the same primary key always go to the same partition, so deduplication and merging keep working correctly. ::: :::tip Note The parentheses in `PARTITION ON COLUMNS (...)` only accept column names (e.g., `device_id`, `area`), not expressions like `device_id + 1`. GreptimeDB does not support MySQL's `PARTITION BY RANGE` syntax. ::: ## Create a partitioned table You can use the MySQL CLI to [connect to GreptimeDB](/user-guide/protocols/mysql.md) and create a distributed table. The following example creates a table and partitions it based on the `device_id` column. ```SQL CREATE TABLE sensor_readings ( device_id INT16, reading_value FLOAT64, ts TIMESTAMP DEFAULT current_timestamp(), PRIMARY KEY (device_id), TIME INDEX (ts) ) PARTITION ON COLUMNS (device_id) ( device_id < 100, device_id >= 100 AND device_id < 200, device_id >= 200 ); ``` More partition columns can be used to create more complex partition rules: ```sql CREATE TABLE sensor_readings ( device_id INT, area STRING, reading_value FLOAT64, ts TIMESTAMP DEFAULT current_timestamp(), PRIMARY KEY (device_id, area), TIME INDEX (ts) ) PARTITION ON COLUMNS (device_id, area) ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South', device_id >= 100 AND area <= 'East', device_id >= 100 AND area > 'East' ); ``` The following content uses the `sensor_readings` table with two partition columns as an example. ## Partition an existing table You can also partition an existing unpartitioned table with `ALTER TABLE ... PARTITION ON COLUMNS`. The syntax is similar to creating a partitioned table, except that the partition rule is added to an existing table: ```sql ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id, area) ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South', device_id >= 100 AND area <= 'East', device_id >= 100 AND area > 'East' ); ``` This is useful when a table starts as a single-partition table and later needs to be distributed across multiple regions. :::warning Warning Partitioning an existing table is a repartitioning operation. It is only supported in distributed clusters and requires shared object storage and GC. For the full prerequisites, see [Repartition](/user-guide/deployments-administration/manage-data/repartition.md#prerequisites). ::: ## Adjust partition rules After a table is partitioned, you can keep tuning its partition layout by splitting hot partitions or merging small cold partitions. Use `SPLIT PARTITION` to split an existing partition into multiple partitions: ```sql ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 AND area < 'South' ) INTO ( device_id < 50 AND area < 'South', device_id >= 50 AND device_id < 100 AND area < 'South' ); ``` Use `MERGE PARTITION` to merge multiple partitions into one: ```sql ALTER TABLE sensor_readings MERGE PARTITION ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ); ``` For prerequisites, hotspot diagnosis, and more step-by-step examples, see [Repartition](/user-guide/deployments-administration/manage-data/repartition.md). ## Insert data into the table The following code inserts 3 rows into each partition of the `sensor_readings` table. ```sql INSERT INTO sensor_readings (device_id, area, reading_value, ts) VALUES (1, 'North', 22.5, '2023-09-19 08:30:00'), (10, 'North', 21.8, '2023-09-19 09:45:00'), (50, 'North', 23.4, '2023-09-19 10:00:00'); INSERT INTO sensor_readings (device_id, area, reading_value, ts) VALUES (20, 'South', 20.1, '2023-09-19 11:15:00'), (40, 'South', 19.7, '2023-09-19 12:30:00'), (90, 'South', 18.9, '2023-09-19 13:45:00'); INSERT INTO sensor_readings (device_id, area, reading_value, ts) VALUES (110, 'East', 25.3, '2023-09-19 14:00:00'), (120, 'East', 26.5, '2023-09-19 15:30:00'), (130, 'East', 27.8, '2023-09-19 16:45:00'); INSERT INTO sensor_readings (device_id, area, reading_value, ts) VALUES (150, 'West', 24.1, '2023-09-19 17:00:00'), (170, 'West', 22.9, '2023-09-19 18:15:00'), (180, 'West', 23.7, '2023-09-19 19:30:00'); ``` :::tip NOTE Note that when the written data does not meet any of the rules in the partitioning scheme, it will be assigned to the default partition (i.e., the first partition 0 of the table). ::: ## Distributed Read Simply use the `SELECT` statement to query the data: ```sql SELECT * FROM sensor_readings order by reading_value desc LIMIT 5; ``` ```sql +-----------+------+---------------+---------------------+ | device_id | area | reading_value | ts | +-----------+------+---------------+---------------------+ | 130 | East | 27.8 | 2023-09-19 16:45:00 | | 120 | East | 26.5 | 2023-09-19 15:30:00 | | 110 | East | 25.3 | 2023-09-19 14:00:00 | | 150 | West | 24.1 | 2023-09-19 17:00:00 | | 180 | West | 23.7 | 2023-09-19 19:30:00 | +-----------+------+---------------+---------------------+ 5 rows in set (0.02 sec) ``` ```sql SELECT MAX(reading_value) AS max_reading FROM sensor_readings; ``` ```sql +-------------+ | max_reading | +-------------+ | 27.8 | +-------------+ 1 row in set (0.03 sec) ``` ```sql SELECT * FROM sensor_readings WHERE area = 'North' AND device_id < 50 ORDER BY device_id; ``` ```sql +-----------+-------+---------------+---------------------+ | device_id | area | reading_value | ts | +-----------+-------+---------------+---------------------+ | 1 | North | 22.5 | 2023-09-19 08:30:00 | | 10 | North | 21.8 | 2023-09-19 09:45:00 | +-----------+-------+---------------+---------------------+ 2 rows in set (0.03 sec) ``` ## Inspect a sharded table GreptimeDB provides severals system table to check DB's state. For table sharding information, you can query [`information_schema.partitions`](/reference/sql/information-schema/partitions.md) which gives the detail of partitions inside one table, and [`information_schema.region_peers`](/reference/sql/information-schema/region-peers.md) which gives the runtime distribution of regions. --- ## Metadata Storage Configuration This section describes how to configure different metadata storage backends for the GreptimeDB Metasrv component. The metadata storage is used to store critical system information including catalogs, schemas, tables, regions, and other metadata that are essential for the operation of GreptimeDB. ## Available Storage Backends GreptimeDB supports the following metadata storage backends: - **etcd**: The default and recommended backend for development and testing environments, offering simplicity and ease of setup - **MySQL/PostgreSQL**: Production-ready backend options that integrate well with existing database infrastructure and cloud RDS services This documentation describes the TOML configuration for each backend. You can use these configurations when deploying GreptimeDB without Helm Chart. If you are using Helm Chart to deploy GreptimeDB, please refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md#configuring-metasrv-backend-storage) for more details. ## Use etcd as metadata storage While etcd is suitable for development and testing environments, it may not be ideal for production deployments requiring high availability and scalability. Configure the metasrv component to use etcd as metadata storage: ```toml # The metadata storage backend for metasrv backend = "etcd_store" # Store server addresses # You can specify multiple etcd endpoints for high availability store_addrs = ["127.0.0.1:2379"] # Backend client options for etcd [backend_client] # The keep alive timeout for backend client keep_alive_timeout = "3s" # The keep alive interval for backend client keep_alive_interval = "10s" # The connect timeout for backend client connect_timeout = "3s" [backend_tls] # - "disable" - No TLS # - "require" - Require TLS mode = "prefer" # Path to client certificate file (for client authentication) # Like "/path/to/client.crt" cert_path = "" # Path to client private key file (for client authentication) # Like "/path/to/client.key" key_path = "" # Path to CA certificate file (for server certificate verification) # Required when using custom CAs or self-signed certificates # Leave empty to use system root certificates only # Like "/path/to/ca.crt" ca_cert_path = "" ``` ### Best Practices While etcd can be used as metadata storage, we recommend against using it in production environments unless you have extensive experience with etcd operations and maintenance. For detailed guidance on etcd management, including installation, backup, and maintenance procedures, please refer to [Manage etcd](/user-guide/deployments-administration/manage-metadata/manage-etcd.md). When using etcd as metadata storage: - Deploy multiple endpoints across different availability zones for high availability - Configure appropriate auto-compaction settings to manage storage growth - Implement regular maintenance procedures: - Run `Defrag` command periodically to reclaim disk space - Monitor etcd cluster health metrics - Review and adjust resource limits based on usage patterns ## Use MySQL as metadata storage MySQL serves as a viable metadata storage backend option. This choice is particularly beneficial when you need to integrate with existing MySQL infrastructure or have specific MySQL-related requirements. For production deployments, we strongly recommend utilizing cloud providers' Relational Database Service (RDS) solutions for enhanced reliability and managed service benefits. Configure the metasrv component to use MySQL as metadata storage: ```toml # The metadata storage backend for metasrv backend = "mysql_store" # Store server address # Format: mysql://user:password@ip:port/dbname store_addrs = ["mysql://user:password@ip:port/dbname"] # Optional: Custom table name for storing metadata # Default: greptime_metakv meta_table_name = "greptime_metakv" [backend_tls] # - "disable" - No TLS # - "prefer" (default) - Try TLS, fallback to plain # - "require" - Require TLS # - "verify_ca" - Require TLS and verify CA # - "verify_full" - Require TLS and verify hostname mode = "prefer" # Path to client certificate file (for client authentication) # Like "/path/to/client.crt" cert_path = "" # Path to client private key file (for client authentication) # Like "/path/to/client.key" key_path = "" # Path to CA certificate file (for server certificate verification) # Required when using custom CAs or self-signed certificates # Leave empty to use system root certificates only # Like "/path/to/ca.crt" ca_cert_path = "" ``` When sharing a MySQL instance between multiple GreptimeDB clusters, you must set a unique `meta_table_name` for each GreptimeDB cluster to avoid metadata conflicts. ## Use PostgreSQL as metadata storage PostgreSQL serves as a viable metadata storage backend option. This choice is particularly beneficial when you need to integrate with existing PostgreSQL infrastructure or have specific PostgreSQL-related requirements. For production deployments, we strongly recommend utilizing cloud providers' Relational Database Service (RDS) solutions for enhanced reliability and managed service benefits. Configure the metasrv component to use PostgreSQL as metadata storage: ```toml # The metadata storage backend for metasrv backend = "postgres_store" # Store server address # Format: password=password dbname=postgres user=postgres host=localhost port=5432 store_addrs = ["password=password dbname=postgres user=postgres host=localhost port=5432"] # Optional: Custom table name for storing metadata # Default: greptime_metakv meta_table_name = "greptime_metakv" # Optional: The schema for metadata table and election table name. # In PostgreSQL 15 and later, the default public schema is restricted by default, # and non-superusers are no longer allowed to create tables in the public schema. # When encountering permission restrictions, use this parameter to specify a writable schema. meta_schema_name = "greptime_schema" # Optional: Automatically create PostgreSQL schema if it doesn't exist. # When enabled, the system will execute `CREATE SCHEMA IF NOT EXISTS ` # before creating metadata tables. This is useful in production environments where # manual schema creation may be restricted. # Default: true # Note: The PostgreSQL user must have CREATE SCHEMA permission for this to work. auto_create_schema = true # Optional: Advisory lock ID for election # Default: 1 meta_election_lock_id = 1 [backend_tls] # - "disable" - No TLS # - "prefer" (default) - Try TLS, fallback to plain # - "require" - Require TLS # - "verify_ca" - Require TLS and verify CA # - "verify_full" - Require TLS and verify hostname mode = "prefer" # Path to client certificate file (for client authentication) # Like "/path/to/client.crt" cert_path = "" # Path to client private key file (for client authentication) # Like "/path/to/client.key" key_path = "" # Path to CA certificate file (for server certificate verification) # Required when using custom CAs or self-signed certificates # Leave empty to use system root certificates only # Like "/path/to/ca.crt" ca_cert_path = "" ``` When sharing a PostgreSQL instance between multiple GreptimeDB clusters or with other applications, you must configure two unique identifiers to prevent conflicts: 1. Set a unique `meta_table_name` for each GreptimeDB cluster to avoid metadata conflicts 2. Assign a unique `meta_election_lock_id` to each GreptimeDB cluster to prevent advisory lock conflicts with other applications using the same PostgreSQL instance --- ## Manage etcd The GreptimeDB cluster requires an etcd cluster for [metadata storage](https://docs.greptime.com/nightly/contributor-guide/metasrv/overview) by default. Let's install an etcd cluster using Bitnami's etcd Helm [chart](https://github.com/bitnami/charts/tree/main/bitnami/etcd). ## Prerequisites - [Kubernetes](https://kubernetes.io/docs/setup/) >= v1.23 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 ## Install Save the following configuration as a file `etcd.yaml`: ```yaml global: security: allowInsecureImages: true replicaCount: 3 image: registry: docker.io repository: greptime/etcd tag: 3.6.1-debian-12-r3 auth: rbac: create: false token: enabled: false persistence: storageClass: null size: 8Gi resources: limits: cpu: '2' memory: 8Gi requests: cpu: '2' memory: 8Gi autoCompactionMode: "revision" autoCompactionRetention: "100" extraEnvVars: - name: ETCD_QUOTA_BACKEND_BYTES value: "8589934592" - name: ETCD_ELECTION_TIMEOUT value: "2000" - name: ETCD_SNAPSHOT_COUNT value: "10000" ``` This configuration uses revision-based auto compaction. Every 5 minutes, etcd checks the current revision and compacts up to `current revision - 100`. The retained history window depends on the write rate; clients that read or watch older revisions must handle compaction errors. Compaction makes space available for reuse within etcd; reclaiming disk space requires defragmentation. Install etcd cluster: ```bash helm upgrade --install etcd \ oci://registry-1.docker.io/bitnamicharts/etcd \ --create-namespace \ --version 12.0.8 \ -n etcd-cluster \ --values etcd.yaml ``` Wait for etcd cluster to be running: ```bash kubectl get pod -n etcd-cluster ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE etcd-0 1/1 Running 0 64s etcd-1 1/1 Running 0 65s etcd-2 1/1 Running 0 72s ```
When the etcd cluster is running, use the following command to check the health status of etcd cluster: ```bash kubectl -n etcd-cluster \ exec etcd-0 -- etcdctl \ --endpoints etcd-0.etcd-headless.etcd-cluster:2379,etcd-1.etcd-headless.etcd-cluster:2379,etcd-2.etcd-headless.etcd-cluster:2379 \ endpoint status -w table ```
Expected Output ```bash +----------------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS | +----------------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ | etcd-0.etcd-headless.etcd-cluster:2379 | 680910587385ae31 | 3.5.15 | 20 kB | false | false | 4 | 73991 | 73991 | | | etcd-1.etcd-headless.etcd-cluster:2379 | d6980d56f5e3d817 | 3.5.15 | 20 kB | false | false | 4 | 73991 | 73991 | | | etcd-2.etcd-headless.etcd-cluster:2379 | 12664fc67659db0a | 3.5.15 | 20 kB | true | false | 4 | 73991 | 73991 | | +----------------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+ ```
## Backup In the bitnami etcd chart, a shared storage volume Network File System (NFS) is used to store etcd backup data. By using CronJob in Kubernetes to perform etcd snapshot backups and mount NFS PersistentVolumeClaim (PVC), snapshots can be transferred to NFS. Add the following configuration and name it `etcd-backup.yaml` file, Note that you need to modify **existingClaim** to your NFS PVC name: ```yaml global: security: allowInsecureImages: true replicaCount: 3 image: registry: docker.io repository: greptime/etcd tag: 3.6.1-debian-12-r3 auth: rbac: create: false token: enabled: false persistence: storageClass: null size: 8Gi resources: limits: cpu: '2' memory: 8Gi requests: cpu: '2' memory: 8Gi autoCompactionMode: "revision" autoCompactionRetention: "100" extraEnvVars: - name: ETCD_QUOTA_BACKEND_BYTES value: "8589934592" - name: ETCD_ELECTION_TIMEOUT value: "2000" - name: ETCD_SNAPSHOT_COUNT value: "10000" # Backup settings disasterRecovery: enabled: true cronjob: schedule: "*/30 * * * *" historyLimit: 2 snapshotHistoryLimit: 2 pvc: existingClaim: "${YOUR_NFS_PVC_NAME_HERE}" ``` Redeploy etcd cluster: ```bash helm upgrade --install etcd \ oci://registry-1.docker.io/bitnamicharts/etcd \ --create-namespace \ --version 12.0.8 \ -n etcd-cluster \ --values etcd-backup.yaml ``` You can see the etcd backup scheduled task: ```bash kubectl get cronjob -n etcd-cluster ```
Expected Output ```bash NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE etcd-snapshotter */30 * * * * False 0 36s ```
```bash kubectl get pod -n etcd-cluster ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE etcd-0 1/1 Running 0 35m etcd-1 1/1 Running 0 36m etcd-2 0/1 Running 0 6m28s etcd-snapshotter-28936038-tsck8 0/1 Completed 0 4m49s ```
```bash kubectl logs etcd-snapshotter-28936038-tsck8 -n etcd-cluster ```
Expected Output ```log etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379 is healthy: successfully committed proposal: took = 2.698457ms etcd 11:18:07.47 INFO ==> Snapshotting the keyspace {"level":"info","ts":"2025-01-06T11:18:07.579095Z","caller":"snapshot/v3_snapshot.go:65","msg":"created temporary db file","path":"/snapshots/db-2025-01-06_11-18.part"} {"level":"info","ts":"2025-01-06T11:18:07.580335Z","logger":"client","caller":"v3@v3.5.15/maintenance.go:212","msg":"opened snapshot stream; downloading"} {"level":"info","ts":"2025-01-06T11:18:07.580359Z","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379"} {"level":"info","ts":"2025-01-06T11:18:07.582124Z","logger":"client","caller":"v3@v3.5.15/maintenance.go:220","msg":"completed snapshot read; closing"} {"level":"info","ts":"2025-01-06T11:18:07.582688Z","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379","size":"20 kB","took":"now"} {"level":"info","ts":"2025-01-06T11:18:07.583008Z","caller":"snapshot/v3_snapshot.go:97","msg":"saved","path":"/snapshots/db-2025-01-06_11-18"} Snapshot saved at /snapshots/db-2025-01-06_11-18 ```
Next, you can see the etcd backup snapshot in the NFS server: ```bash ls ${NFS_SERVER_DIRECTORY} ```
Expected Output ```bash db-2025-01-06_11-18 db-2025-01-06_11-20 db-2025-01-06_11-22 ```
## Restore When you encounter etcd data loss or corruption, such as critical information stored in etcd being accidentally deleted, or catastrophic cluster failure that prevents recovery, you need to perform an etcd restore. Additionally, restoring etcd can also be useful for development and testing purposes. Before recovery, you need to stop writing data to the etcd cluster (stop GreptimeDB Metasrv writing) and create the latest snapshot file use for recovery. Add the following configuration file and name it `etcd-restore.yaml`. Note that **existingClaim** is the name of your NFS PVC, and **snapshotFilename** is change to the etcd snapshot file name: ```yaml global: security: allowInsecureImages: true replicaCount: 3 image: registry: docker.io repository: greptime/etcd tag: 3.6.1-debian-12-r3 auth: rbac: create: false token: enabled: false persistence: storageClass: null size: 8Gi resources: limits: cpu: '2' memory: 8Gi requests: cpu: '2' memory: 8Gi autoCompactionMode: "revision" autoCompactionRetention: "100" extraEnvVars: - name: ETCD_QUOTA_BACKEND_BYTES value: "8589934592" - name: ETCD_ELECTION_TIMEOUT value: "2000" - name: ETCD_SNAPSHOT_COUNT value: "10000" # Restore settings startFromSnapshot: enabled: true existingClaim: "${YOUR_NFS_PVC_NAME_HERE}" snapshotFilename: "${YOUR_ETCD_SNAPSHOT_FILE_NAME}" ``` Deploy etcd recover cluster: ```bash helm upgrade --install etcd-recover \ oci://registry-1.docker.io/bitnamicharts/etcd \ --create-namespace \ --version 12.0.8 \ -n etcd-cluster \ --values etcd-restore.yaml ``` After waiting for the etcd recover cluster to be Running: ```bash kubectl get pod -n etcd-cluster -l app.kubernetes.io/instance=etcd-recover ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE etcd-recover-0 1/1 Running 0 91s etcd-recover-1 1/1 Running 0 91s etcd-recover-2 1/1 Running 0 91s ```
:::tip NOTE The configuration structure has changed between chart versions: - In older version: `meta.etcdEndpoints` - In newer version: `meta.backendStorage.etcd.endpoints` Always refer to the latest [values.yaml](https://github.com/GreptimeTeam/helm-charts/blob/main/charts/greptimedb-cluster/values.yaml) in the Helm chart repository for the most up-to-date configuration structure. ::: Next, change Metasrv [backendStorage.etcd.endpoints](https://github.com/GreptimeTeam/helm-charts/tree/main/charts/greptimedb-cluster) to the new etcd recover cluster, in this example is `"etcd-recover.etcd-cluster.svc.cluster.local:2379"`: ```yaml apiVersion: greptime.io/v1alpha1 kind: GreptimeDBCluster metadata: name: greptimedb spec: # Other configuration here meta: backendStorage: etcd: endpoints: - "etcd-recover.etcd-cluster.svc.cluster.local:2379" ``` Restart GreptimeDB Metasrv to complete etcd restore. ## Monitoring - Prometheus Operator installed (e.g. via [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack)). - podmonitor CRD installed (automatically installed with Prometheus Operator). Add the following to your `etcd-monitoring.yaml` to enable monitoring: ```yaml global: security: allowInsecureImages: true replicaCount: 3 image: registry: docker.io repository: greptime/etcd tag: 3.6.1-debian-12-r3 auth: rbac: create: false token: enabled: false persistence: storageClass: null size: 8Gi resources: limits: cpu: '2' memory: 8Gi requests: cpu: '2' memory: 8Gi autoCompactionMode: "revision" autoCompactionRetention: "100" extraEnvVars: - name: ETCD_QUOTA_BACKEND_BYTES value: "8589934592" - name: ETCD_ELECTION_TIMEOUT value: "2000" - name: ETCD_SNAPSHOT_COUNT value: "10000" # Monitoring settings metrics: enabled: true podMonitor: enabled: true namespace: etcd-cluster interval: 10s scrapeTimeout: 10s additionalLabels: release: prometheus ``` Deploy etcd with Monitoring: ```bash helm upgrade --install etcd \ oci://registry-1.docker.io/bitnamicharts/etcd \ --create-namespace \ --version 12.0.8 \ -n etcd-cluster \ --values etcd-monitoring.yaml ``` ### Grafana dashboard Use the [ETCD Cluster Overview dashboard](https://grafana.com/grafana/dashboards/15308-etcd-cluster-overview/) (ID: 15308) for monitoring key metrics. 1. Log in your Grafana. 2. Navigate to Dashboards -> New -> Import. 3. Enter Dashboard ID: 15308, select the data source and load. ![ETCD Cluster Overview dashboard](/etcd-cluster-overview-dashboard.png) ## ⚠️ Defrag - Critical Warning Defragmentation is a HIGH-RISK operation that can severely impact your ETCD cluster and dependent systems (like GreptimeDB): 1. Blocks ALL read/write operations during execution (cluster becomes unavailable). 2. High I/O usage may cause timeouts in client applications. 3. May trigger leader elections if defrag takes too long. 4. Can cause OOM kills if not properly resourced. 5. May corrupt data if interrupted mid-process. ETCD uses a multi-version concurrency control (MVCC) mechanism that stores multiple versions of KV. Over time, as data is updated and deleted, the backend database can become fragmented, leading to increased storage usage and reduced performance. Defragmentation is the process of compacting this storage to reclaim space and improve performance. Add the following defrag-related configuration to `etcd-defrag.yaml` file: ```yaml global: security: allowInsecureImages: true replicaCount: 3 image: registry: docker.io repository: greptime/etcd tag: 3.6.1-debian-12-r3 auth: rbac: create: false token: enabled: false persistence: storageClass: null size: 8Gi resources: limits: cpu: '2' memory: 8Gi requests: cpu: '2' memory: 8Gi autoCompactionMode: "revision" autoCompactionRetention: "100" extraEnvVars: - name: ETCD_QUOTA_BACKEND_BYTES value: "8589934592" - name: ETCD_ELECTION_TIMEOUT value: "2000" - name: ETCD_SNAPSHOT_COUNT value: "10000" # Defragmentation settings defrag: enabled: true cronjob: schedule: "0 3 * * *" # Daily at 3:00 AM suspend: false successfulJobsHistoryLimit: 1 failedJobsHistoryLimit: 1 ``` Deploying with Defrag Configuration: ```bash helm upgrade --install etcd \ oci://registry-1.docker.io/bitnamicharts/etcd \ --create-namespace \ --version 12.0.8 \ -n etcd-cluster \ --values etcd-defrag.yaml ``` You can see the etcd defrag scheduled task: ```bash kubectl get cronjob -n etcd-cluster ```
Expected Output ```bash NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE etcd-defrag 0 3 * * * False 0 34s ```
```bash kubectl get pod -n etcd-cluster ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE etcd-0 1/1 Running 0 4m30s etcd-1 1/1 Running 0 4m29s etcd-2 1/1 Running 0 4m29s etcd-defrag-29128518-sstbf 0/1 Completed 0 90s ```
```bash kubectl logs etcd-defrag-29128518-sstbf -n etcd-cluster ```
Expected Output ```log Finished defragmenting etcd member[http://etcd-0.etcd-headless.etcd-cluster.svc.cluster.local:2379] Finished defragmenting etcd member[http://etcd-1.etcd-headless.etcd-cluster.svc.cluster.local:2379] Finished defragmenting etcd member[http://etcd-2.etcd-headless.etcd-cluster.svc.cluster.local:2379] ```
--- ## Metadata Storage A GreptimeDB cluster stores its metadata in an external system, which the Metasrv component reads and writes. The supported backends are: - **[etcd](https://etcd.io/)** — a distributed key-value store with a small operational footprint. - **[MySQL](https://www.mysql.com/) and [PostgreSQL](https://www.postgresql.org/)** — relational databases with ACID transactions, replication, and established backup tooling. Both are available as managed services (RDS) on the major cloud platforms. ## Which to choose For development and testing, etcd is sufficient. For production, use a cloud provider's managed relational database. A managed service covers high availability, automated backups, and maintenance, which is where most of the operational cost of self-hosting a metadata store goes. Whichever backend you run, it sits outside GreptimeDB's own data path: it needs its own backup schedule and its own health monitoring. ## Next steps - [Configuration](/user-guide/deployments-administration/manage-metadata/configuration.md) — pointing Metasrv at a backend. - [Manage etcd](/user-guide/deployments-administration/manage-metadata/manage-etcd.md) — running etcd for development and testing. --- ## Backup and Restore, Migrate GreptimeDB provides metadata backup and restore capabilities through its CLI tool. This functionality supports all major metadata storage backends including etcd, MySQL, and PostgreSQL. For detailed instructions on using these features, refer to the [Backup and Restore](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md) guide. ## Backup For optimal backup reliability, schedule metadata backups during periods of low DDL (Data Definition Language) activity. This helps ensure data consistency and reduces the risk of partial or incomplete backups. To perform a backup: 1. Verify that your GreptimeDB cluster is operational 2. Execute the backup using the CLI tool, follows the export metadata steps in [Backup and Restore](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md) guide. 3. Ensure the backup output file is created, and the file size is greater than 0. ## Restore To restore from a backup: 1. Use the CLI tool to restore the metadata, follows the import metadata steps in [Backup and Restore](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md) guide. 2. Restart the GreptimeDB cluster to apply the restored metadata. 3. Set the [Next Table ID](/user-guide/deployments-administration/maintenance/sequence-management.md) to the original cluster's next table ID. 4. Call the [Table Reconciliation](/user-guide/deployments-administration/maintenance/table-reconciliation.md) function to repair the table metadata inconsistency. ## Migrate We recommend to migrate metadata during periods of low DDL (Data Definition Language) activity to ensure data consistency and minimize the risk of partial or incomplete migrations. Migrate metadata from one metadata storage to another. 1. Backup the metadata from the source storage. 2. Restore the metadata to the target storage. 3. Restart the whole GreptimeDB cluster(all components) to apply the restored metadata. --- ## Migrate Local SQL File Access SQL access to local files is sandboxed in standalone deployments and disabled in distributed deployments. ## Standalone The default sandbox is `/copy`. Relative paths in `COPY` and external-table locations are resolved below this directory. Absolute paths work only when they are inside the sandbox. Before upgrading, identify existing `COPY` workflows and external tables that use local paths outside the default sandbox. Choose one of these migrations: - Move the files below `/copy` and update the SQL locations. - Set `storage.copy_root` to a dedicated local directory containing the files. - Move the files to S3, OSS, GCS, or AzBlob and update the SQL locations. Do not set `storage.copy_root` to `storage.data_home` or to a directory that contains GreptimeDB data, WAL, manifests, or configuration files. GreptimeDB rejects copy roots that expose its internal data directory. When `storage.data_home` is an object-storage URL, local SQL file access is disabled unless `storage.copy_root` explicitly names a local directory. ## Distributed Distributed frontend and datanode processes reject local paths for `COPY TABLE`, `COPY QUERY`, `COPY DATABASE`, and external tables. Migrate these workflows and tables to S3, OSS, GCS, or AzBlob before upgrading. --- ## Check GreptimeDB Status GreptimeDB provides a series of HTTP endpoints to query the operational status of GreptimeDB. The following HTTP requests assume that GreptimeDB is running on node `127.0.0.1` with the HTTP service listening on the default port `4000`. ## Check if GreptimeDB is running normally You can use the `/health` endpoint to check if GreptimeDB is running normally. An HTTP status code `200 OK` indicates that GreptimeDB is running normally. Example: ```bash curl -i -X GET http://127.0.0.1:4000/health ``` Output: ```text HTTP/1.1 200 OK content-type: application/json vary: origin, access-control-request-method, access-control-request-headers access-control-allow-origin: * content-length: 2 date: Sun, 26 Apr 2026 13:46:41 GMT {} ``` For more information about the health check endpoint, please refer to [the Health endpoint](/reference/http-endpoints.md#health-check). ## Check GreptimeDB status You can use the `/status` endpoint to check the deployment status of GreptimeDB. ```bash curl -X GET http://127.0.0.1:4000/status | jq ``` Output: ```json { "commit": "8d2f92c01ae762287a3cac587156519a536ae134", "branch": "", "rustc_version": "rustc 1.96.0-nightly (ac7f9ec7d 2026-03-20)", "hostname": "127.0.0.1", "version": "1.0.1" } ``` Please refer to [the Status endpoint](/reference/http-endpoints.md#status) for more details. --- ## Self-Monitoring GreptimeDB Clusters Before reading this document, ensure you understand how to [deploy a GreptimeDB cluster on Kubernetes](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md). This guide will walk you through configuring monitoring when deploying a GreptimeDB cluster. ## Quick Start You can enable monitoring and Grafana by adding configurations to the `values.yaml` file when deploying the GreptimeDB cluster using Helm Chart. Here's a complete example of `values.yaml` for deploying a minimal GreptimeDB cluster with monitoring and Grafana: ```yaml image: # Image registry: # Use `docker.io` for OSS GreptimeDB, # consult staff for Enterprise GreptimeDB registry: # Image repository: # Use `greptime/greptimedb` for OSS GreptimeDB # Consult staff for GreptimeDB Enterprise repository: # Image tag: # Use database version `v1.2.1` for OSS GreptimeDB # Consult staff for GreptimeDB Enterprise tag: pullSecrets: [] initializer: registry: docker.io repository: greptime/greptimedb-initializer tag: "v0.6.1" monitoring: # Enable monitoring enabled: true grafana: # Enable Grafana deployment # Requires monitoring to be enabled first (monitoring.enabled: true) enabled: true frontend: replicas: 1 meta: replicas: 1 backendStorage: etcd: endpoints: ["etcd.etcd-cluster.svc.cluster.local:2379"] datanode: replicas: 1 flownode: replicas: 1 ``` When `monitoring` is enabled, GreptimeDB Operator launches an additional GreptimeDB Standalone instance to collect metrics and logs from the GreptimeDB cluster. To collect log data, GreptimeDB Operator starts a [Vector](https://vector.dev/) sidecar container in each Pod. When `grafana` is enabled, a Grafana instance is deployed that uses the GreptimeDB Standalone instance configured for cluster monitoring as its data source. This enables visualization of the GreptimeDB cluster's monitoring data out of the box using both Prometheus and MySQL protocols. Then install the GreptimeDB cluster with the above `values.yaml` file: ```bash helm upgrade --install mycluster \ greptime/greptimedb-cluster \ --values /path/to/values.yaml \ -n default ``` After installation, you can check the Pod status of the GreptimeDB cluster: ```bash kubectl -n default get pods ```
Expected Output ```bash NAME READY STATUS RESTARTS AGE mycluster-datanode-0 2/2 Running 0 77s mycluster-flownode-0 2/2 Running 0 2m26s mycluster-frontend-6ffdd549b-9s7gx 2/2 Running 0 66s mycluster-grafana-675b64786-ktqps 1/1 Running 0 6m35s mycluster-meta-58bc88b597-ppzvj 2/2 Running 0 86s mycluster-monitor-standalone-0 1/1 Running 0 6m35s ```
You can then access the Grafana dashboard by port-forwarding the Grafana service to your local machine: ```shell kubectl -n default port-forward svc/mycluster-grafana 18080:80 ``` Then refer to the [Access Grafana Dashboard](#access-grafana-dashboard) section below for details on accessing Grafana. ## Monitoring Configuration This section covers the details of monitoring configurations. ### Enable Monitoring Add the following configuration to [`values.yaml`](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md#setup-valuesyaml) to enable monitoring when deploying the GreptimeDB cluster: ```yaml monitoring: enabled: true ``` This deploys a `GreptimeDBStandalone` resource named `${cluster-name}-monitor` to collect metrics and logs. You can verify the deployment with: ```bash kubectl get greptimedbstandalones.greptime.io ${cluster-name}-monitor -n ${namespace} ``` The Service and StatefulSet it creates are named `${cluster-name}-monitor-standalone`, and the Pods add the usual StatefulSet ordinal, for example `${cluster-name}-monitor-standalone-0`. You can use the following addresses to access monitoring data: - **Prometheus metrics**: `http://${cluster-name}-monitor-standalone.${namespace}.svc.cluster.local:4000/v1/prometheus` - **SQL logs**: `${cluster-name}-monitor-standalone.${namespace}.svc.cluster.local:4002`. By default, cluster logs are stored in the `public._gt_logs` table. ### Customize Monitoring Storage By default, the GreptimeDB Standalone instance stores monitoring data using the Kubernetes default StorageClass in local storage. You can configure the GreptimeDB Standalone instance through the `monitoring.standalone` field in `values.yaml`. For example, the following configuration uses S3 object storage to store monitoring data: ```yaml monitoring: enabled: true ttl: 30d standalone: base: main: # Configure GreptimeDB Standalone instance image image: "greptime/greptimedb:v1.2.1" # Configure GreptimeDB Standalone instance resources resources: requests: cpu: "2" memory: "4Gi" limits: cpu: "2" memory: "4Gi" # Configure object storage for GreptimeDB Standalone instance objectStorage: s3: # Configure bucket bucket: "monitoring" # Configure region region: "ap-southeast-1" # Configure secret name secretName: "s3-credentials" # Configure root path root: "standalone-with-s3-data" ``` ### Customize Vector Sidecar The Vector sidecar configuration for log collection can be customized via the `monitoring.vector` field. For example, you can adjust the Vector image and resource limits as follows: ```yaml monitoring: enabled: true ttl: 30d vector: # Configure Vector image registry registry: docker.io # Configure Vector image repository repository: timberio/vector # Configure Vector image tag tag: "0.49.0-debian" # Configure Vector resources resources: requests: cpu: "50m" memory: "64Mi" limits: cpu: "250m" memory: "256Mi" ``` ### YAML Configuration with `kubectl` Deployment If you're not using Helm Chart, you can also use the `monitoring` field to manually configure self-monitoring mode in the `GreptimeDBCluster` YAML: ```yaml monitoring: enabled: true ``` For detailed configuration options, refer to the [`GreptimeDBCluster` API documentation](https://github.com/GreptimeTeam/greptimedb-operator/blob/main/docs/api-references/docs.md#monitoringspec). ## Grafana Configuration ### Enable Grafana To enable Grafana deployment, add the following configuration to `values.yaml`. Note that monitoring must be enabled first [(`monitoring.enabled: true`)](#enable-monitoring): ```yaml monitoring: enabled: true grafana: enabled: true ``` ### Customize Grafana Data Sources By default, Grafana uses `mycluster` and `default` as the cluster name and namespace to create data sources. To monitor clusters with different names or namespaces, you need to create custom data source configurations based on the actual cluster names and namespaces. Here's an example `values.yaml` configuration: ```yaml monitoring: enabled: true grafana: enabled: true datasources: datasources.yaml: datasources: # Query the cluster metrics. - name: greptimedb-metrics type: prometheus url: http://${cluster-name}-monitor-standalone.${namespace}.svc.cluster.local:4000/v1/prometheus access: proxy # Query the cluster traces. - name: greptimedb-traces type: jaeger url: http://${cluster-name}-monitor-standalone.${namespace}.svc.cluster.local:4000/v1/jaeger access: proxy # Query the cluster logs and slow queries. - name: greptimedb-logs type: mysql url: ${cluster-name}-monitor-standalone.${namespace}.svc.cluster.local:4002 access: proxy database: public # Query the information schema from the cluster. - name: information_schema type: mysql url: ${cluster-name}-frontend.${namespace}.svc.cluster.local:4002 access: proxy database: information_schema ``` This configuration creates the following data sources for GreptimeDB cluster monitoring in Grafana: - **`greptimedb-metrics`**: Cluster metrics stored in the standalone monitoring database, exposed via Prometheus protocol (`type: prometheus`) - **`greptimedb-logs`**: Cluster logs stored in the standalone monitoring database, exposed via MySQL protocol (`type: mysql`). Uses the `public` database by default - **`greptimedb-traces`**: Cluster traces stored in the standalone monitoring database, exposed via Jaeger protocol (`type: jaeger`). Uses the `public` database by default - **`information_schema`**: Cluster information stored in the frontend database, exposed via MySQL protocol (`type: mysql`). Uses the `information_schema` database by default ### Access Grafana Dashboard You can access the Grafana dashboard by port-forwarding the Grafana service to your local machine: ```bash kubectl -n ${namespace} port-forward svc/${cluster-name}-grafana 18080:80 ``` Then open `http://localhost:18080` to access the Grafana dashboard. The default login credentials for Grafana are: - **Username**: `admin` - **Password**: `gt-operator` Navigate to the `Dashboards` section to explore the pre-configured dashboards for monitoring your GreptimeDB cluster. ![Grafana Dashboard](/kubernetes-cluster-grafana-dashboard.jpg) ## Cleanup the PVCs :::danger This removes the collected metrics and logs stored by the monitoring standalone instance. It does not touch the data of the GreptimeDB cluster itself, but the monitoring history is not recoverable afterwards. ::: To uninstall the GreptimeDB cluster, please refer to the [Cleanup GreptimeDB Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md#cleanup) documentation. To clean up the Persistent Volume Claims (PVCs) used by the GreptimeDB standalone monitoring instance, delete the PVCs using the following command: ```bash kubectl -n ${namespace} delete pvc -l app.greptime.io/component=${cluster-name}-monitor-standalone ``` --- ## DDL events See [Event data model](/user-guide/deployments-administration/monitoring/events/event-data-model.md) for shared columns and Procedure states. ## Database events ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, json_to_string(payload) AS payload FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND type = '' ORDER BY timestamp; ``` Database event types are `create_database`, `alter_database`, and `drop_database`. Event rows retain `schema_name`, which this query uses as a filter but does not include in the result. **`create_database`** ```sql +-------------------------------+-----------------+-----------------+--------------+----------------------------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | payload | +-------------------------------+-----------------+-----------------+--------------+----------------------------------------------------------------------------------+ | 2026-08-10 11:39:18.014737397 | create_database | Running | Submitted | {"create_if_not_exists":true,"options":[{"key":"ttl","value":"1h"}],"version":1} | | 2026-08-10 11:39:18.052757527 | create_database | Done | Succeeded | null | +-------------------------------+-----------------+-----------------+--------------+----------------------------------------------------------------------------------+ ``` **`alter_database`** ```sql +-------------------------------+----------------+-----------------+--------------+---------------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | payload | +-------------------------------+----------------+-----------------+--------------+---------------------------------------------------------------------+ | 2026-08-10 11:39:18.060198497 | alter_database | Running | Submitted | {"action":"set","options":[{"key":"ttl","value":"2h"}],"version":1} | | 2026-08-10 11:39:18.107764363 | alter_database | Done | Succeeded | null | +-------------------------------+----------------+-----------------+--------------+---------------------------------------------------------------------+ ``` **`drop_database`** ```sql +-------------------------------+---------------+-----------------+--------------+-------------------------------------+ | timestamp | type | procedure_state | trigger_type | payload | +-------------------------------+---------------+-----------------+--------------+-------------------------------------+ | 2026-08-10 11:39:18.113713574 | drop_database | Running | Submitted | {"drop_if_exists":true,"version":1} | | 2026-08-10 11:39:18.197868233 | drop_database | Done | Succeeded | null | +-------------------------------+---------------+-----------------+--------------+-------------------------------------+ ``` ## Table events ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, table_name, table_id, json_to_string(payload) AS payload FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND type = '' ORDER BY timestamp; ``` This query returns table events of the selected type from the specified database in the last hour. To query one table, add `AND table_name = ''` to the `WHERE` clause. Rows for the same table retain `table_name`. A successful `create_table` or `create_logical_tables` event includes `table_id` only when the Procedure returns the allocated ID. `alter_table`, `truncate_table`, and `drop_table` already know the table ID when they are submitted, so their event rows include it. GreptimeDB also records `create_logical_tables` and `alter_logical_tables` events. Each emits one row per logical table for each lifecycle trigger type, so a query by `procedure_id` can return multiple rows for the same trigger type. `undrop_table` and `purge_dropped_table` are Enterprise-only. **`create_table`** ```sql +-------------------------------+--------------+-----------------+--------------+----------------+----------+------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | table_name | table_id | payload | +-------------------------------+--------------+-----------------+--------------+----------------+----------+------------------------------------------------------------+ | 2026-08-10 11:39:53.544351589 | create_table | Running | Submitted | ddl_source | NULL | {"create_if_not_exists":false,"engine":"mito","version":1} | | 2026-08-10 11:39:53.620080844 | create_table | Done | Succeeded | ddl_source | 1449 | null | +-------------------------------+--------------+-----------------+--------------+----------------+----------+------------------------------------------------------------+ ``` **`alter_table`** ```sql +-------------------------------+-------------+-----------------+--------------+------------+----------+------------------------------------+ | timestamp | type | procedure_state | trigger_type | table_name | table_id | payload | +-------------------------------+-------------+-----------------+--------------+------------+----------+------------------------------------+ | 2026-08-10 11:40:23.445906161 | alter_table | Running | Submitted | ddl_source | 1449 | {"kind":"add_columns","version":1} | | 2026-08-10 11:40:23.539710789 | alter_table | Done | Succeeded | ddl_source | 1449 | null | +-------------------------------+-------------+-----------------+--------------+------------+----------+------------------------------------+ ``` **`truncate_table`** ```sql +-------------------------------+----------------+-----------------+--------------+----------------+----------+------------------------------------+ | timestamp | type | procedure_state | trigger_type | table_name | table_id | payload | +-------------------------------+----------------+-----------------+--------------+----------------+----------+------------------------------------+ | 2026-08-10 11:40:51.949349425 | truncate_table | Running | Submitted | ddl_drop_probe | 1451 | {"time_range_count":0,"version":1} | | 2026-08-10 11:40:51.982366731 | truncate_table | Done | Succeeded | ddl_drop_probe | 1451 | null | +-------------------------------+----------------+-----------------+--------------+----------------+----------+------------------------------------+ ``` **`drop_table`** ```sql +-------------------------------+------------+-----------------+--------------+----------------+----------+--------------------------------------+ | timestamp | type | procedure_state | trigger_type | table_name | table_id | payload | +-------------------------------+------------+-----------------+--------------+----------------+----------+--------------------------------------+ | 2026-08-10 11:40:51.986072827 | drop_table | Running | Submitted | ddl_drop_probe | 1451 | {"drop_if_exists":false,"version":1} | | 2026-08-10 11:40:52.061732144 | drop_table | Done | Succeeded | ddl_drop_probe | 1451 | null | +-------------------------------+------------+-----------------+--------------+----------------+----------+--------------------------------------+ ``` ## Flow events Flow rows retain `catalog_name` and `flow_name`. `schema_name` is SQL `NULL` for Flow events, while `flow_name` remains available. `create_flow` adds `flow_id` when available, and `drop_flow` retains the known ID. ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, catalog_name, schema_name, flow_name, flow_id, json_to_string(payload) AS payload FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND flow_name = '' AND type = '' ORDER BY timestamp; ``` **`create_flow`** ```sql +-------------------------------+-------------+-----------------+--------------+--------------+-------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | catalog_name | schema_name | flow_name | flow_id | payload | +-------------------------------+-------------+-----------------+--------------+--------------+-------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ | 2026-08-10 11:41:47.833758897 | create_flow | Running | Submitted | greptime | NULL | ddl_flow | NULL | {"create_if_not_exists":false,"eval_interval_secs":10,"expire_after":null,"or_replace":false,"version":1} | | 2026-08-10 11:41:47.857319802 | create_flow | Done | Succeeded | greptime | NULL | ddl_flow | 1025 | null | +-------------------------------+-------------+-----------------+--------------+--------------+-------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ ``` **`drop_flow`** ```sql +-------------------------------+-----------+-----------------+--------------+--------------+-------------+-----------+---------+--------------------------------------+ | timestamp | type | procedure_state | trigger_type | catalog_name | schema_name | flow_name | flow_id | payload | +-------------------------------+-----------+-----------------+--------------+--------------+-------------+-----------+---------+--------------------------------------+ | 2026-08-10 11:41:47.864231473 | drop_flow | Running | Submitted | greptime | NULL | ddl_flow | 1025 | {"drop_if_exists":false,"version":1} | | 2026-08-10 11:41:47.926665304 | drop_flow | Done | Succeeded | greptime | NULL | ddl_flow | 1025 | null | +-------------------------------+-----------+-----------------+--------------+--------------+-------------+-----------+---------+--------------------------------------+ ``` ## View events View rows retain stable `view_name` values. `create_view` adds `view_id` only when available, and `drop_view` retains the known ID. ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, view_name, view_id, json_to_string(payload) AS payload FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND view_name = '' AND type = '' ORDER BY timestamp; ``` The query uses `schema_name` as a filter and does not include it in the result. **`create_view`** ```sql +-------------------------------+-------------+-----------------+--------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | view_name | view_id | payload | +-------------------------------+-------------+-----------------+--------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ | 2026-08-10 11:41:18.483028192 | create_view | Running | Submitted | ddl_view | NULL | {"column_count":0,"create_if_not_exists":false,"or_replace":false,"referenced_table_count":1,"version":1} | | 2026-08-10 11:41:18.513571854 | create_view | Done | Succeeded | ddl_view | 1452 | null | +-------------------------------+-------------+-----------------+--------------+-----------+---------+-----------------------------------------------------------------------------------------------------------+ ``` **`drop_view`** ```sql +-------------------------------+-----------+-----------------+--------------+-----------+---------+--------------------------------------+ | timestamp | type | procedure_state | trigger_type | view_name | view_id | payload | +-------------------------------+-----------+-----------------+--------------+-----------+---------+--------------------------------------+ | 2026-08-10 11:41:18.520281932 | drop_view | Running | Submitted | ddl_view | 1452 | {"drop_if_exists":false,"version":1} | | 2026-08-10 11:41:18.572632167 | drop_view | Done | Succeeded | ddl_view | 1452 | null | +-------------------------------+-----------+-----------------+--------------+-----------+---------+--------------------------------------+ ``` --- ## Event data model `greptime_private.events` has the following common columns. | Column | Meaning | | --------------- | ----------------------------------------------------------- | | `type` | Event type, such as `create_table` or `region_migration`. | | `timestamp` | Time at which the row was recorded. | | `payload` | JSON data for the event type. | | `actor` | Database user recorded as the operation's initiator; SQL `NULL` when the event is not associated with a user request. | | `event_context` | JSON describing why the event was triggered when available. | ## Event actor {#actor} The `actor` is the database user recorded as the initiator of an operation. Use it to identify who ran an `ADMIN` statement or submitted a Procedure. When an operation starts a Procedure, GreptimeDB records the database user as its `actor`. The Procedure and any child Procedures it starts keep the same `actor` throughout their lifecycle. Events that are not associated with a user request have a SQL `NULL` actor. When [authentication](/user-guide/deployments-administration/authentication/overview.md) is enabled, `actor` is the authenticated database user. Without authentication, `actor` does not verify who sent the request. MySQL and PostgreSQL record the username supplied by the client, while HTTP and gRPC record the default user `greptime`. ## Procedure event columns Procedure events also have the following columns: | Column | Meaning | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `procedure_id` | Unique Procedure ID. | | `procedure_state` | Procedure state when the event was recorded. Values are `Running`, `Done`, `Retrying`, `PrepareRollback`, `RollingBack`, `Failed`, and `Poisoned`. | | `procedure_trigger` | Procedure event trigger in JSON. Its `type` is `Submitted`, `Recovered`, `ChildSubmitted`, `Retrying`, `RollingBack`, `Succeeded`, `Failed`, or `Poisoned`. | | `procedure_error` | Debug-formatted error when the Procedure fails; an empty string otherwise. | Rows with trigger type `Submitted` normally have state `Running`. When a Procedure succeeds, the completed event has state `Done` and trigger type `Succeeded`. The completed row is generated from the Procedure's final state, so its fields can differ from the submitted row. Events are recorded asynchronously; a recording failure does not change the Procedure result. `event_context` is written only on `Submitted` rows. When it is available, its stable `reason` value is one of `manual`, `auto_create`, `auto_alter`, `auto_repartition`, `auto_rebalance`, `region_failover`, `scheduled_gc`, or `unknown`. For example, a MySQL-submitted event can contain `{"protocol":"mysql","reason":"manual"}`. In addition to `Submitted` and `Succeeded`, a Procedure can emit the following trigger types when applicable. Not every Procedure emits every trigger type, and rows are not guaranteed to appear in the order shown: | `type` | Meaning and fields | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Recovered` | The root Procedure was recovered from persisted state. | | `ChildSubmitted` | A child Procedure submission was attempted. `procedure_trigger` includes the child `procedure_id` and its `outcome` (`Accepted`, `AlreadyAccepted`, `ManagerStopped`, or `SpawnFailed`). | | `Retrying` | Procedure execution or rollback is being retried. `procedure_trigger` includes the retry `phase` (`Execute` or `Rollback`) and `attempt`. | | `RollingBack` | Procedure rollback is starting. | | `Failed` | The Procedure reached a failed terminal state. Inspect `procedure_error` for failure details. | | `Poisoned` | The Procedure cannot proceed. Inspect `procedure_error` for the failure details. | ## ADMIN function event columns An `admin_function` event records the result returned by an `ADMIN` statement. | Column | Meaning | | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | `admin_function_name` | The name of the executed ADMIN function. | | `admin_function_status` | The execution status: `Succeeded` or `Failed`. | | `admin_function_output` | JSON containing `result` when the function succeeds or `error` when it fails. | The event `payload` contains the function arguments. If the result is a Procedure ID, query the Procedure events for that ID to track its progress. ## Query JSON fields See the [JSON functions](/reference/sql/functions/json.md) reference for details. In event queries, `json_to_string` converts a JSON value to readable text, `json_get_string` extracts a value by path, `json_path_match` evaluates a JSON predicate, and `json_is_null` checks whether a value is the JSON `null` value. Use `IS NULL` separately to check for SQL `NULL`. For example, this query extracts fields from a `create_table` row that contains `event_context`: ```sql SELECT procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, json_path_match(procedure_trigger, '$.type == "Submitted"') AS is_submitted, json_get_string(event_context, 'reason') AS reason FROM greptime_private.events WHERE type = 'create_table' AND timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' AND event_context IS NOT NULL ORDER BY timestamp; ``` ```sql +-----------------+--------------+--------------+--------+ | procedure_state | trigger_type | is_submitted | reason | +-----------------+--------------+--------------+--------+ | Running | Submitted | 1 | manual | +-----------------+--------------+--------------+--------+ ``` ## JSON `null` and SQL `NULL` In the `create_table` example and DDL/repartition events after a Procedure completes, a terminal `payload` can be JSON `null` rather than SQL `NULL`: ```sql SELECT procedure_state, json_to_string(payload) AS payload, payload IS NULL AS payload_is_sql_null, json_is_null(payload) AS payload_is_json_null, json_get_string(procedure_trigger, 'type') AS trigger_type FROM greptime_private.events WHERE type = 'create_table' AND timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' ORDER BY timestamp; ``` ```sql +-----------------+------------------------------------------------------------+---------------------+----------------------+--------------+ | procedure_state | payload | payload_is_sql_null | payload_is_json_null | trigger_type | +-----------------+------------------------------------------------------------+---------------------+----------------------+--------------+ | Running | {"create_if_not_exists":false,"engine":"mito","version":1} | 0 | 0 | Submitted | | Done | null | 0 | 1 | Succeeded | +-----------------+------------------------------------------------------------+---------------------+----------------------+--------------+ ``` ## Procedure event type-specific columns The following columns are populated only by the listed event types. An SQL `NULL` value in one of these columns is normal when it does not apply. - **Database, table, and view events:** `catalog_name`, `schema_name`, and the applicable table or view name and ID columns identify the affected object. Flow events use `catalog_name` and `flow_name`; their `schema_name` is SQL `NULL`. `physical_table_id` applies only to `create_logical_tables` and `alter_logical_tables`. See [DDL events](/user-guide/deployments-administration/monitoring/events/ddl-events.md). - **Region migration:** `region_id` and `region_number` identify the Region. `region_migration_trigger_reason`, `region_migration_src_node_id`, `region_migration_src_peer_addr`, `region_migration_dst_node_id`, and `region_migration_dst_peer_addr` describe why and where it moved. - **Repartition:** `catalog_name`, `schema_name`, `table_name`, and `table_id` identify the affected table. `parent_procedure_id` links a child procedure to its parent; `repartition_group_id` identifies a group operation. `source_region_id`, `source_region_number`, `source_partition_expr`, `target_region_id`, `target_region_number`, and `target_partition_expr` describe the affected Regions and partition expressions. - **Batch GC:** `region_id`, `region_number`, and `gc_report` describe the Regions processed and their GC result. - **WAL pruning:** `topic_name`, `prunable_entry_id`, and `latest_offset` identify the topic, requested prune boundary, and exclusive latest offset. --- ## Maintenance events Metasrv records GC and Remote WAL pruning events in distributed deployments. ## GC events Each row with a non-`NULL` `gc_report` records the files processed for one Region: ```sql SELECT timestamp, procedure_id, procedure_state, region_id, table_id, region_number, json_to_string(gc_report) AS gc_report FROM greptime_private.events WHERE type = 'batch_gc' AND gc_report IS NOT NULL AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 3; ``` `deleted_files` lists deleted SST/Parquet file IDs. `deleted_indexes` lists deleted index file IDs and their `index_version`. `need_retry` is `true` when the Region needs another GC run. For example: ```json { "deleted_files": ["580653aa-252b-415b-aaf9-ce65e9d78249"], "deleted_indexes": [ {"file_id":"580653aa-252b-415b-aaf9-ce65e9d78249","index_version":0} ], "need_retry": false } ``` Scheduled GC does not emit a `Submitted` row. A manual `Submitted` row contains the configuration. If no Region needs cleanup or retry, no `Done` row is written. `payload` is JSON `null` on a report row. ## Remote WAL pruning ```sql SELECT timestamp, type, topic_name, prunable_entry_id, latest_offset, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, json_to_string(payload) AS payload FROM greptime_private.events WHERE type = 'wal_prune' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 3; ``` ```sql +-------------------------------+-----------+-------------------------+-------------------+---------------+-----------------+--------------+--------------------------------------+ | timestamp | type | topic_name | prunable_entry_id | latest_offset | procedure_state | trigger_type | payload | +-------------------------------+-----------+-------------------------+-------------------+---------------+-----------------+--------------+--------------------------------------+ | 2026-08-10 11:23:28.495676149 | wal_prune | greptimedb_wal_topic_20 | 248376 | 248381 | Done | Succeeded | {"logical_delete":false,"version":1} | | 2026-08-10 10:53:28.478381545 | wal_prune | greptimedb_wal_topic_20 | 248375 | 248376 | Done | Succeeded | {"logical_delete":false,"version":1} | | 2026-08-10 10:23:28.486761751 | wal_prune | greptimedb_wal_topic_20 | 248371 | 248375 | Done | Succeeded | {"logical_delete":false,"version":1} | +-------------------------------+-----------+-------------------------+-------------------+---------------+-----------------+--------------+--------------------------------------+ ``` `prunable_entry_id` is the pruning boundary for `topic_name`. It is the smallest boundary reported by Regions that use the topic, so none of those Regions needs entries before it. On a non-`Succeeded` row, it is only the attempted boundary. When present, `latest_offset` is the Kafka latest offset observed during this attempt. It is an exclusive upper bound: the offset after the last record, not the offset of the last record. The recorder omits `Submitted` and `Recovered` rows. A successful run writes `Done`; `Retrying` is not terminal, and a run can also write `Failed` or `Poisoned`. --- ## Events The `greptime_private.events` table stores events recorded while GreptimeDB runs. It helps operators inspect DDL changes and background procedures without searching service logs. GreptimeDB writes event records asynchronously on a best-effort basis. Records are normally flushed every five seconds, so a recent operation might not be visible immediately. Event recording should not be treated as the operation's success acknowledgement. ## Configure event recording See [Event recording](/user-guide/deployments-administration/configuration.md#event-recording) for configuration options and the event types supported by standalone, Frontend, and Metasrv. Standalone deployments record supported local DDL Procedure events. Distributed deployments with Metasrv can additionally record operational event types. ## Query events The table is created when the first event is recorded. If no event has been recorded yet, or event recording is disabled, this query returns a table-not-found error. Start with a bounded query while investigating a recent operation: ```sql SELECT timestamp, type FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` Use the following pages for focused queries and event details: - [Query events](/user-guide/deployments-administration/monitoring/events/query-events.md) - [Event data model](/user-guide/deployments-administration/monitoring/events/event-data-model.md) - [DDL events](/user-guide/deployments-administration/monitoring/events/ddl-events.md) - [Region events](/user-guide/deployments-administration/monitoring/events/region-events.md) - [Maintenance events](/user-guide/deployments-administration/monitoring/events/maintenance-events.md) --- ## Query events Query the `greptime_private.events` system table to investigate recent events. Events are written asynchronously, so a newly submitted operation might not be visible immediately. See [Event data model](/user-guide/deployments-administration/monitoring/events/event-data-model.md) for event columns. ## Start with recent events The following query returns all columns for up to 20 events recorded during the last hour: ```sql SELECT * FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` This is useful for exploration, but it returns every column. For routine checks, select only the columns you need: ```sql SELECT timestamp, type, json_to_string(payload) AS payload FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` The compact result keeps the time, type, and payload visible without pasting the full output. ## Discover and filter event types List types actually present in the cluster before choosing a filter: ```sql SELECT type, COUNT(*) AS event_rows FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour GROUP BY type ORDER BY type; ``` The result is a point-in-time view of the event types currently present in the cluster. It varies with workload and does not define the configured or source-supported types. See [DDL events](/user-guide/deployments-administration/monitoring/events/ddl-events.md) for the supported local DDL event types. ## Filter events by actor Use `actor` to list recent events recorded for a database user: ```sql SELECT timestamp, type, actor, json_to_string(payload) AS payload FROM greptime_private.events WHERE actor = '' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` See [Event actor](/user-guide/deployments-administration/monitoring/events/event-data-model.md#actor) for how GreptimeDB determines this value. ## Query ADMIN function events An `admin_function` event records the function name, the current database user, the immediate status, the input arguments, and the immediate output: ```sql SELECT timestamp, actor, admin_function_name, admin_function_status, json_to_string(payload) AS payload, json_to_string(admin_function_output) AS output FROM greptime_private.events WHERE type = 'admin_function' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` For a successful function, `output` contains a `result`. For a failed function, it contains an `error`: ```text | actor | admin_function_name | admin_function_status | output | | root | flush_table | Succeeded | {"result":0} | | root | unknown_function | Failed | {"error":"..."} | ``` Combine an event type with a database and object name to avoid unrelated rows: ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type FROM greptime_private.events WHERE type = 'create_table' AND timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' ORDER BY timestamp; ``` Example output: ```sql +-------------------------------+--------------+-----------------+--------------+ | timestamp | type | procedure_state | trigger_type | +-------------------------------+--------------+-----------------+--------------+ | 2026-08-10 11:28:40.590240203 | create_table | Running | Submitted | | 2026-08-10 11:28:40.659064297 | create_table | Done | Succeeded | +-------------------------------+--------------+-----------------+--------------+ ``` ## Find the latest event for an object Replace the placeholders with the object name and, where needed, the database. Each query returns the newest matching event. ### Database ```sql SELECT timestamp, type, schema_name, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND type IN ('create_database', 'alter_database', 'drop_database') ORDER BY timestamp DESC LIMIT 1; ``` ### Table ```sql SELECT timestamp, type, schema_name, table_name, table_id, procedure_state FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' ORDER BY timestamp DESC LIMIT 1; ``` ### Flow ```sql SELECT timestamp, type, flow_name, flow_id, procedure_state FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND flow_name = '' ORDER BY timestamp DESC LIMIT 1; ``` ### View ```sql SELECT timestamp, type, schema_name, view_name, view_id, procedure_state FROM greptime_private.events WHERE timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND view_name = '' ORDER BY timestamp DESC LIMIT 1; ``` ### Region Operational events that reference a Region are global, not tied to a database. Use this query to find the latest event for a Region. It requires `region_migration`, `batch_gc`, and `repartition_group` to have each recorded at least one event: the table adds an event type's columns when it first records that type. A row fills only the fields for its event type; the other selected fields are SQL `NULL`. ```sql SELECT timestamp, type, procedure_state, region_id, source_region_id, target_region_id, region_migration_trigger_reason, region_migration_src_node_id, region_migration_dst_node_id FROM greptime_private.events WHERE type IN ('region_migration', 'batch_gc', 'repartition_group') AND timestamp >= now() - INTERVAL '1' hour AND (region_id = OR source_region_id = OR target_region_id = ) ORDER BY timestamp DESC LIMIT 1; ``` ## Query Procedure events Procedure events share a `procedure_id`. ### Get a procedure ID For a table-creation procedure, find the row whose trigger type is `Submitted` for the given database and table: ```sql SELECT procedure_id FROM greptime_private.events WHERE type = 'create_table' AND timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' AND json_path_match(procedure_trigger, '$.type == "Submitted"') ORDER BY timestamp DESC LIMIT 1; ``` Example result: ```sql +--------------------------------------+ | procedure_id | +--------------------------------------+ | a5788f51-5726-4db7-a85e-e9afc36da557 | +--------------------------------------+ ``` Use the returned ID to query the Procedure's event rows. Filtering by `schema_name` and `table_name` avoids selecting a procedure for another object with a similar name. ### Query a Procedure Use the full-row query when you need to explore every available column: ```sql SELECT * FROM greptime_private.events WHERE procedure_id = '' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp ASC; ``` For routine checks, use a focused projection: ```sql SELECT timestamp, type, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, procedure_error, json_to_string(payload) AS payload FROM greptime_private.events WHERE procedure_id = '' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp; ``` Example output from a MySQL operation: ```sql +-------------------------------+--------------+-----------------+--------------+-----------------+------------------------------------------------------------+ | timestamp | type | procedure_state | trigger_type | procedure_error | payload | +-------------------------------+--------------+-----------------+--------------+-----------------+------------------------------------------------------------+ | 2026-08-10 11:23:14.388632208 | create_table | Running | Submitted | | {"create_if_not_exists":false,"engine":"mito","version":1} | | 2026-08-10 11:23:14.463992155 | create_table | Done | Succeeded | | null | +-------------------------------+--------------+-----------------+--------------+-----------------+------------------------------------------------------------+ ``` ### Find failed Procedures To list recent failed Procedures: ```sql SELECT timestamp, type, procedure_id, procedure_state, json_get_string(procedure_trigger, 'type') AS trigger_type, procedure_error FROM greptime_private.events WHERE procedure_state IN ('Failed', 'Poisoned') AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` --- ## Region events Metasrv records Region events for repartitioning and Region migration in distributed deployments. A `region_id` combines a high 32-bit `table_id` with a low 32-bit `region_number`. The queries on this page return all three values together. ## Repartition Each repartition consists of a root `repartition` Procedure and one or more `repartition_group` child Procedures. The root `Submitted` row records the table and requested partition rules. The child `Submitted` rows show how each source Region is split or merged. Find the root Procedure: ```sql SELECT timestamp, procedure_id, procedure_state, catalog_name, schema_name, table_name, table_id, json_to_string(payload) AS payload FROM greptime_private.events WHERE type = 'repartition' AND timestamp >= now() - INTERVAL '1' hour AND schema_name = '' AND table_name = '' AND json_path_match(procedure_trigger, '$.type == "Submitted"') ORDER BY timestamp DESC LIMIT 1; ``` ```sql +-------------------------------+--------------------------------------+-----------------+--------------+-------------------+-------------------------+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | timestamp | procedure_id | procedure_state | catalog_name | schema_name | table_name | table_id | payload | +-------------------------------+--------------------------------------+-----------------+--------------+-------------------+-------------------------+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | 2026-08-09 08:29:30.771436255 | 95bb4e53-e73e-40b1-b878-208b0ae2b812 | Running | greptime | repartitiondebug1 | greptime_physical_table | 1332 | {"source_partition_exprs":["namespace >= app-2"],"source_type":"partitioned","target_partition_exprs":["namespace >= app-2 AND namespace < app-26","namespace >= app-26 AND namespace < app-5","namespace >= app-5"],"timeout":"2m","version":2} | +-------------------------------+--------------------------------------+-----------------+--------------+-------------------+-------------------------+----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ``` The `payload` column contains `source_type` and `target_partition_exprs`. It also contains `source_partition_exprs` for a partitioned source. Use the returned `procedure_id` to query the Region mapping: ```sql SELECT timestamp, parent_procedure_id AS repartition_procedure_id, procedure_id AS repartition_group_procedure_id, source_region_id, table_id AS source_table_id, source_region_number, source_partition_expr, target_region_id, table_id AS target_table_id, target_region_number, target_partition_expr FROM greptime_private.events WHERE type = 'repartition_group' AND parent_procedure_id = '' AND json_path_match(procedure_trigger, '$.type == "Submitted"') ORDER BY source_region_id, target_region_id; ``` ```sql +-------------------------------+--------------------------------------+--------------------------------------+------------------+-----------------+----------------------+-----------------------+------------------+-----------------+----------------------+-------------------------------------------+ | timestamp | repartition_procedure_id | repartition_group_procedure_id | source_region_id | source_table_id | source_region_number | source_partition_expr | target_region_id | target_table_id | target_region_number | target_partition_expr | +-------------------------------+--------------------------------------+--------------------------------------+------------------+-----------------+----------------------+-----------------------+------------------+-----------------+----------------------+-------------------------------------------+ | 2026-08-09 08:29:30.968778963 | 95bb4e53-e73e-40b1-b878-208b0ae2b812 | 4f132d57-c402-4c9f-b707-3b9d53b32cfa | 5720896438274 | 1332 | 2 | namespace >= app-2 | 5720896438274 | 1332 | 2 | namespace >= app-2 AND namespace < app-26 | | 2026-08-09 08:29:30.968778963 | 95bb4e53-e73e-40b1-b878-208b0ae2b812 | 4f132d57-c402-4c9f-b707-3b9d53b32cfa | 5720896438274 | 1332 | 2 | namespace >= app-2 | 5720896438275 | 1332 | 3 | namespace >= app-26 AND namespace < app-5 | | 2026-08-09 08:29:30.968778963 | 95bb4e53-e73e-40b1-b878-208b0ae2b812 | 4f132d57-c402-4c9f-b707-3b9d53b32cfa | 5720896438274 | 1332 | 2 | namespace >= app-2 | 5720896438276 | 1332 | 4 | namespace >= app-5 | +-------------------------------+--------------------------------------+--------------------------------------+------------------+-----------------+----------------------+-----------------------+------------------+-----------------+----------------------+-------------------------------------------+ ``` For an unpartitioned table, `source_partition_expr` is SQL `NULL`. The source and target table IDs are the same because every mapping belongs to one table. To inspect a child Procedure's lifecycle, query its `procedure_id` on [Query Procedure events](/user-guide/deployments-administration/monitoring/events/query-events.md). ## Region migration Region migration can be started manually or by Region Balancer or Region Failover. This query returns all Region rows for the latest migration event: ```sql WITH latest_migration AS ( SELECT procedure_id, timestamp FROM greptime_private.events WHERE type = 'region_migration' AND timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 1 ) SELECT e.timestamp, e.procedure_id, e.procedure_state, e.region_id, e.table_id, e.region_number, e.region_migration_trigger_reason, e.region_migration_src_node_id AS source_datanode_id, e.region_migration_src_peer_addr AS source_datanode_addr, e.region_migration_dst_node_id AS target_datanode_id, e.region_migration_dst_peer_addr AS target_datanode_addr FROM greptime_private.events AS e JOIN latest_migration AS latest ON e.procedure_id = latest.procedure_id AND e.timestamp = latest.timestamp WHERE e.type = 'region_migration' ORDER BY e.region_id; ``` ```sql +-------------------------------+--------------------------------------+-----------------+---------------+----------+---------------+---------------------------------+--------------------+----------------------+--------------------+----------------------+ | timestamp | procedure_id | procedure_state | region_id | table_id | region_number | region_migration_trigger_reason | source_datanode_id | source_datanode_addr | target_datanode_id | target_datanode_addr | +-------------------------------+--------------------------------------+-----------------+---------------+----------+---------------+---------------------------------+--------------------+----------------------+--------------------+----------------------+ | 2026-08-10 02:52:50.078350103 | 63167e21-0165-4964-afef-02271baa126b | Done | 5162550689808 | 1202 | 16 | AutoRebalance | 1 | 172.16.62.49:4001 | 2 | 172.16.232.158:4001 | +-------------------------------+--------------------------------------+-----------------+---------------+----------+---------------+---------------------------------+--------------------+----------------------+--------------------+----------------------+ ``` `region_migration_trigger_reason` identifies why the migration started. The source and target columns identify the Datanodes involved. --- ## Key Logs During operation, GreptimeDB outputs key operations and unexpected error information to logs. You can use these logs to understand GreptimeDB's operational status and troubleshoot errors. ## Log Location GreptimeDB components default to outputting INFO level logs to the following locations: - Standard output - `greptimedb_data/logs` directory under GreptimeDB's current working directory The log output directory can also be modified through the `[logging]` section in the configuration file or the `--log-dir` startup parameter: ```toml [logging] dir = "/path/to/logs" ``` Log file formats are: - `greptimedb.YYYY-MM-DD-HH` contains logs of INFO level and above - `greptimedb-err.YYYY-MM-DD-HH` contains error logs For example: ```bash greptimedb.2025-04-11-06 greptimedb-err.2025-04-11-06 ``` Currently, GreptimeDB components include: - frontend - datanode - metasrv - flownode If you need to adjust log levels, such as viewing DEBUG level logs for a component, you can refer to [this document](https://github.com/GreptimeTeam/greptimedb/blob/main/docs/how-to/how-to-change-log-level-on-the-fly.md) to modify them at runtime. ## Important Logs The following lists recommended logs to monitor: ### Error Logs When the database is running normally and stably, it will not output error logs. If database operations encounter exceptions or panics occur, error logs will be printed. It is recommended that users check error logs from all components. #### Panic If the database encounters a panic, it is recommended to collect the panic logs and report them to the official team. Typical panic logs look like this, with the keyword `panicked at`: ```bash 2025-04-02T14:44:24.485935Z ERROR common_telemetry::panic_hook: panicked at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/expr/src/logical_plan/plan.rs:1035:25: with_new_exprs for Distinct does not support sort expressions backtrace= 0: backtrace::backtrace::libunwind::trace at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/backtrace/libunwind.rs:116:5 backtrace::backtrace::trace_unsynchronized at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/backtrace/mod.rs:66:5 1: backtrace::backtrace::trace at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/backtrace/mod.rs:53:14 2: backtrace::capture::Backtrace::create at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/capture.rs:292:9 3: backtrace::capture::Backtrace::new at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/backtrace-0.3.74/src/capture.rs:257:22 4: common_telemetry::panic_hook::set_panic_hook::{{closure}} at /greptime/codes/greptime/procedure-traits/src/common/telemetry/src/panic_hook.rs:37:25 5: as core::ops::function::Fn>::call at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/alloc/src/boxed.rs:1984:9 std::panicking::rust_panic_with_hook at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/std/src/panicking.rs:820:13 6: std::panicking::begin_panic_handler::{{closure}} at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/std/src/panicking.rs:678:13 7: std::sys::backtrace::__rust_end_short_backtrace at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/std/src/sys/backtrace.rs:168:18 8: rust_begin_unwind at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/std/src/panicking.rs:676:5 9: core::panicking::panic_fmt at /rustc/409998c4e8cae45344fd434b358b697cc93870d0/library/core/src/panicking.rs:75:14 10: datafusion_expr::logical_plan::plan::LogicalPlan::with_new_exprs at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/expr/src/logical_plan/plan.rs:1035:25 11: ::analyze::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/optimizer/type_conversion.rs:105:17 12: core::ops::function::impls::::call_mut at /greptime/.rustup/toolchains/nightly-2024-12-25-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:272:13 13: core::ops::function::impls::::call_once at /greptime/.rustup/toolchains/nightly-2024-12-25-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:305:13 14: datafusion_common::tree_node::Transformed::transform_parent at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/common/src/tree_node.rs:764:44 15: datafusion_common::tree_node::TreeNode::transform_up::transform_up_impl::{{closure}} at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/common/src/tree_node.rs:265:13 16: stacker::maybe_grow at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/stacker-0.1.17/src/lib.rs:55:9 datafusion_common::tree_node::TreeNode::transform_up::transform_up_impl at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/common/src/tree_node.rs:260:9 17: datafusion_common::tree_node::TreeNode::transform_up at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/common/src/tree_node.rs:269:9 18: datafusion_common::tree_node::TreeNode::transform at /greptime/.cargo/git/checkouts/datafusion-11a8b534adb6bd68-shallow/2464703/datafusion/common/src/tree_node.rs:220:9 19: ::analyze at /greptime/codes/greptime/procedure-traits/src/query/src/optimizer/type_conversion.rs:46:9 20: query::query_engine::state::QueryEngineState::optimize_by_extension_rules::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/query_engine/state.rs:195:17 21: core::iter::traits::iterator::Iterator::try_fold at /greptime/.rustup/toolchains/nightly-2024-12-25-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:2370:21 22: query::query_engine::state::QueryEngineState::optimize_by_extension_rules at /greptime/codes/greptime/procedure-traits/src/query/src/query_engine/state.rs:192:9 23: query::planner::DfLogicalPlanner::plan_sql::{{closure}}::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/planner.rs:119:20 24: as core::future::future::Future>::poll at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-0.1.40/src/instrument.rs:321:9 25: query::planner::DfLogicalPlanner::plan_sql::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/planner.rs:71:5 26: ::plan::{{closure}}::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/planner.rs:198:73 27: as core::future::future::Future>::poll at /greptime/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-0.1.40/src/instrument.rs:321:9 28: ::plan::{{closure}} at /greptime/codes/greptime/procedure-traits/src/query/src/planner.rs:195:5 ... ``` ### Metasrv When the GreptimeDB cluster experiences node online/offline events, region migration, schema changes, etc., metasrv will record corresponding logs. Therefore, in addition to error logs from each component, it is also recommended to monitor the following metasrv log keywords: #### Metasrv Leader Step Down/Election ```bash // Error level, indicates current leader stepped down, new election will follow. Note the {:?} part is the leader identifier "Leader :{:?} step down" ``` #### Region Lease Renewal Failure ```bash // Warning level, indicates a region lease renewal was denied. Region lease requests will be rejected when the region is not properly closed/scheduled on a datanode. Denied to renew region lease for datanode: {datanode_id}, region_id: {region_id} ``` ```bash // Info level, datanode receives lease renewal failure and attempts to close target region Closing staled region: ``` #### Region Failover ```bash // Warning level, detects some regions failed Phi health check, need to execute failover operation. The datanode ID and region IDs will be printed after the colon Detects region failures on datanode: // A region migration failed Failed to wait region migration procedure // Info level, when maintenance mode is enabled, failover procedure will be skipped Maintenance mode is enabled, skip failover ``` #### Region Migration ```bash // Info level, indicates a region starts migration. Region information will be printed after the log Starting region migration procedure // Error level, a region migration failed Failed to wait region migration procedure ``` #### Procedure Metasrv internally uses a component called "procedure" to execute distributed operations. You can monitor error logs from this component: ```bash Failed to execute procedure ``` #### Flow Creation Failure When flow creation fails, the failure reason can usually be seen in procedure error logs. Logs may contain the following keywords: ```bash Failed to execute procedure metasrv-procedure:: CreateFlow ``` ### Datanode #### Compaction When compaction starts and ends, datanode will log the following information: ```bash 2025-05-16T06:01:08.794415Z INFO mito2::compaction::task: Compacted SST files, region_id: 4612794875904(1074, 0), input: [FileMeta { region_id: 4612794875904(1074, 0), file_id: FileId(a29500fb-cae0-4f3f-8376-cb3f14653378), time_range: (1686455010000000000::Nanosecond, 1686468410000000000::Nanosecond), level: 0, file_size: 45893329, available_indexes: [], index_file_size: 0, num_rows: 5364000, num_row_groups: 53, sequence: Some(114408000) }, FileMeta { region_id: 4612794875904(1074, 0), file_id: FileId(a31dcb1b-19ae-432f-8482-9e1b7db7b53b), time_range: (1686468420000000000::Nanosecond, 1686481820000000000::Nanosecond), level: 0, file_size: 45900506, available_indexes: [], index_file_size: 0, num_rows: 5364000, num_row_groups: 53, sequence: Some(119772000) }], output: [FileMeta { region_id: 4612794875904(1074, 0), file_id: FileId(5d105ca7-9e3c-4298-afb3-e85baae3b2e8), time_range: (1686455010000000000::Nanosecond, 1686481820000000000::Nanosecond), level: 1, file_size: 91549797, available_indexes: [], index_file_size: 0, num_rows: 10728000, num_row_groups: 105, sequence: Some(119772000) }], window: Some(86400), waiter_num: 0, merge_time: 3.034328293s ``` ```bash 2025-05-16T06:01:08.805366Z INFO mito2::request: Successfully compacted region: 4612794875904(1074, 0) ``` --- ## Prometheus-Monitoring GreptimeDB Cluster Before reading this document, ensure you understand how to [deploy a GreptimeDB cluster on Kubernetes](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md). It is recommended to use [self-monitoring mode](cluster-monitoring-deployment.md) to monitor GreptimeDB cluster, as it's simple to set up and provides out-of-the-box Grafana dashboards. However, if you already have a Prometheus instance deployed in your Kubernetes cluster and want to integrate GreptimeDB cluster metrics into it, follow the steps below. ## Check the Prometheus Instance Configuration Ensure you have deployed the Prometheus Operator and created a Prometheus instance. For example, you can use [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) to deploy the Prometheus stack. Refer to its [official documentation](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) for more details. When deploying the Prometheus instance, ensure you set the labels used for scraping GreptimeDB cluster metrics. For example, your existing Prometheus instance may contain the following configuration: ```yaml apiVersion: monitoring.coreos.com/v1 kind: PodMonitor metadata: name: greptime-podmonitor namespace: default spec: selector: matchLabels: release: prometheus # other configurations... ``` When the `PodMonitor` is deployed, the Prometheus Operator continuously watches for pods in the `default` namespace that match all labels defined in `spec.selector.matchLabels` (in this example, `release: prometheus`). ## Enable `prometheusMonitor` for GreptimeDB Cluster When deploying a GreptimeDB cluster using a Helm Chart, enable the `prometheusMonitor` field in your `values.yaml` file. For example: ```yaml prometheusMonitor: # Enable Prometheus monitoring - this will create PodMonitor resources enabled: true # Configure scrape interval interval: "30s" # Configure labels labels: release: prometheus ``` **Important:** The `labels` field value (`release: prometheus`) must match the `matchLabels` field used to create the Prometheus instance, otherwise metrics collection won't work properly. After configuring `prometheusMonitor`, the GreptimeDB Operator will automatically create `PodMonitor` resources and import metrics into Prometheus at the specified `interval`. You can check the `PodMonitor` resources with: ``` kubectl get podmonitors.monitoring.coreos.com -n ${namespace} ``` :::note If you're not using a Helm Chart, you can manually configure Prometheus monitoring in the `GreptimeDBCluster` YAML: ```yaml apiVersion: greptime.io/v1alpha1 kind: GreptimeDBCluster metadata: name: basic spec: base: main: image: greptime/greptimedb:v1.2.1 frontend: replicas: 1 meta: replicas: 1 backendStorage: etcd: endpoints: - "etcd.etcd-cluster.svc.cluster.local:2379" datanode: replicas: 1 prometheusMonitor: enabled: true interval: "30s" labels: release: prometheus ``` ::: ## Grafana Dashboards You need to deploy Grafana by yourself, then import the dashboards. ### Add Data Sources After deploying Grafana, refer to Grafana's [data sources](https://grafana.com/docs/grafana/latest/datasources/) documentation to add the following two type data sources: - **Prometheus**: Name it `metrics`. This data source connects to your Prometheus instance, which collects GreptimeDB cluster monitoring metrics. Use your Prometheus instance URL as the connection URL. - **MySQL**: Name it `information-schema`. This data source connects to your GreptimeDB cluster to access cluster metadata via the SQL protocol. If you have deployed GreptimeDB following the [Deploy a GreptimeDB Cluster on Kubernetes](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md) guide, use `${cluster-name}-frontend.${namespace}.svc.cluster.local:4002` as the server address with database `information_schema`. ### Import Dashboards The [GreptimeDB Cluster Metrics Dashboard](https://github.com/GreptimeTeam/greptimedb/tree/v1.2.1/grafana/dashboards/metrics/cluster) uses the `metrics` and `information-schema` data sources to display GreptimeDB cluster metrics. Refer to Grafana's [Import dashboards](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/import-dashboards/) documentation to learn how to import dashboards. --- ## Monitoring Effective database administration relies heavily on monitoring. You can monitor GreptimeDB using the following methods: --- ## Runtime Information The `INFORMATION_SCHEMA` database provides access to system metadata, such as the name of a database or table, the data type of a column, etc. * Find the topology information of the cluster though [CLUSTER_INFO](/reference/sql/information-schema/cluster-info.md) table. * Find the table regions distribution though [PARTITIONS](/reference/sql/information-schema/partitions.md) and [REGION_PEERS](/reference/sql/information-schema/region-peers.md) tables. For example, find all the region id of a table: ```sql SELECT greptime_partition_id FROM PARTITIONS WHERE table_name = 'monitor' ``` Find the distribution of all regions in a table: ```sql SELECT b.peer_id as datanode_id, a.greptime_partition_id as region_id FROM information_schema.partitions a LEFT JOIN information_schema.region_peers b ON a.greptime_partition_id = b.region_id WHERE a.table_name='monitor' ORDER BY datanode_id ASC ``` For more information about the `INFORMATION_SCHEMA` database, Please read the [reference](/reference/sql/information-schema/overview.md). --- ## Slow Query (Experimental) GreptimeDB provides a slow query log to help you find and fix slow queries. By default, the slow queries are output to the system table `greptime_private.slow_queries` with `30s` threshold and `1.0` sample ratio with `30d` TTL. The schema of the `greptime_private.slow_queries` table is as follows: ```sql +--------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------+----------------------+------+------+---------+---------------+ | cost | UInt64 | | NO | | FIELD | | threshold | UInt64 | | NO | | FIELD | | query | String | | NO | | FIELD | | is_promql | Boolean | | NO | | FIELD | | timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | promql_range | UInt64 | | NO | | FIELD | | promql_step | UInt64 | | NO | | FIELD | | promql_start | TimestampMillisecond | | NO | | FIELD | | promql_end | TimestampMillisecond | | NO | | FIELD | +--------------+----------------------+------+------+---------+---------------+ ``` - `cost`: The cost of the query in milliseconds. - `threshold`: The threshold of the query in milliseconds. - `query`: The query string. - `is_promql`: Whether the query is a PromQL query. - `timestamp`: The timestamp of the query. - `promql_range`: The range of the query. Only used when `is_promql` is `true`. - `promql_step`: The step of the query. Only used when `is_promql` is `true`. - `promql_start`: The start time of the query. Only used when `is_promql` is `true`. - `promql_end`: The end time of the query. Only used when `is_promql` is `true`. In cluster mode, you can configure the slow query in frontend configs (same as in standalone mode), for example: ```toml [slow_query] ## Whether to enable slow query log. enable = true ## The record type of slow queries. It can be `system_table` or `log`. ## If `system_table` is selected, the slow queries will be recorded in a system table `greptime_private.slow_queries`. ## If `log` is selected, the slow queries will be logged in a log file `greptimedb-slow-queries.*`. record_type = "system_table" ## The threshold of slow query. It can be human readable time string, for example: `10s`, `100ms`, `1s`. threshold = "30s" ## The sampling ratio of slow query log. The value should be in the range of (0, 1]. For example, `0.1` means 10% of the slow queries will be logged and `1.0` means all slow queries will be logged. sample_ratio = 1.0 ## The TTL of the `slow_queries` system table. Default is `30d` when `record_type` is `system_table`. ttl = "30d" ``` If you use the Helm chart to deploy GreptimeDB, you can configure the slow query in the `values.yaml` file, for example: ```yaml slowQuery: enable: true recordType: "system_table" threshold: "30s" sampleRatio: "1.0" ttl: "30d" ``` If you use `log` as the record type, the slow queries will be logged in a log file `greptimedb-slow-queries.*`. By default, the log file is located in the `${data_home}/logs` directory. --- ## Standalone Monitoring GreptimeDB standalone provides a `/metrics` endpoint on the HTTP port (default `4000`) that exposes [Prometheus metrics](/reference/http-endpoints.md#metrics). You can use Prometheus to scrape these metrics and Grafana to visualize them. ## Grafana Dashboard Integration GreptimeDB provides pre-built Grafana dashboards for monitoring standalone deployments. You can access the dashboard JSON files from the [GreptimeDB repository](https://github.com/GreptimeTeam/greptimedb/tree/v1.2.1/grafana/dashboards/metrics/standalone). --- ## Tracing GreptimeDB supports distributed tracing. GreptimeDB exports all collected spans using the gRPC-based OTLP protocol. Users can use [Jaeger](https://www.jaegertracing.io/), [Tempo](https://grafana.com/oss/tempo/) and other OTLP protocol backends that support gRPC to collect the span instrument by GreptimeDB. In the [logging section](/user-guide/deployments-administration/configuration.md#logging-options) in the configuration, there are descriptions of configuration items related to tracing, [standalone.example.toml](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.1/config/standalone.example.toml) provide a reference configuration in the logging section. ## Dynamic Tracing Control GreptimeDB provides the ability to enable or disable tracing dynamically at runtime using the HTTP API without requiring a server restart. This is useful for troubleshooting production issues or temporarily enabling tracing for debugging purposes. To enable tracing: ```bash curl --data "true" http://127.0.0.1:4000/debug/enable_trace # Output: trace enabled ``` To disable tracing: ```bash curl --data "false" http://127.0.0.1:4000/debug/enable_trace # Output: trace disabled ``` ## Tutorial: Use Jaeger to trace GreptimeDB [Jaeger](https://www.jaegertracing.io/) is an open source, end-to-end distributed tracing system, originally developed and open sourced by Uber. Its goal is to help developers monitor and debug the request flow in complex microservice architectures. Jaeger supports gRPC-based OTLP protocol, so GreptimeDB can export trace data to Jaeger. The following tutorial shows you how to deploy and use Jaeger to track GreptimeDB. ### Step 1: Deploy Jaeger Start a Jaeger instance using the `all-in-one` docker image officially provided by jaeger: ```bash docker run --rm -d --name jaeger \ -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ -p 6831:6831/udp \ -p 6832:6832/udp \ -p 5778:5778 \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ -p 14250:14250 \ -p 14268:14268 \ -p 14269:14269 \ -p 9411:9411 \ jaegertracing/all-in-one:latest ``` ### Step 2: Deploy GreptimeDB Write configuration files to allow GreptimeDB to perform tracing. Save the following configuration items as the file `config.toml` ```Toml [logging] enable_otlp_tracing = true ``` Then start GreptimeDB using standalone mode ```bash greptime standalone start -c config.toml ``` Refer to the chapter [MySQL](/user-guide/protocols/mysql.md) on how to connect to GreptimeDB. Run the following SQL statement in MySQL Client: ```sql CREATE TABLE host ( ts timestamp(3) time index, host STRING PRIMARY KEY, val BIGINT, ); INSERT INTO TABLE host VALUES (0, 'host1', 0), (20000, 'host2', 5); SELECT * FROM host ORDER BY ts; DROP TABLE host; ``` ### Step 3: Obtain trace information in Jaeger 1. Go to http://127.0.0.1:16686/ and select the Search tab. 2. Select the `greptime-standalone` service in the service drop-down list. 3. Click **Find Traces** to display trace information. ![JaegerUI](/jaegerui.png) ![Select-tracing](/select-tracing.png) ## Guide: How to configure tracing sampling rate GreptimeDB provides many protocols and interfaces for data insertion, query and other functions. You can collect the calling chains of each operation through tracing. However, for some high-frequency operations, collecting all tracing of the operation may be unnecessary and waste storage space. At this time, you can use `tracing_sample_ratio` to set the sampling rate of tracing for various operations, which can greatly reduce the number of exported tracing and facilitate system observation. All tracing within GreptimeDB is classified according to the protocol it is connected to and the corresponding operations of that protocol: | **protocol** | **request_type** | |--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | grpc | inserts / query.sql / query.logical_plan / query.prom_range / query.empty / ddl.create_database / ddl.create_table / ddl.alter / ddl.drop_table / ddl.truncate_table / ddl.empty / deletes / row_inserts / row_deletes | | mysql | | | postgres | | | otlp | metrics / traces / logs | | opentsdb | | | influxdb | write_v1 / write_v2 | | prometheus | remote_read / remote_write / format_query / instant_query / range_query / labels_query / series_query / label_values_query | | http | sql / promql | You can configure different tracing sampling rates through `tracing_sample_ratio`. ```toml [logging] enable_otlp_tracing = true [logging.tracing_sample_ratio] default_ratio = 0.0 [[logging.tracing_sample_ratio.rules]] protocol = "mysql" ratio = 1.0 [[logging.tracing_sample_ratio.rules]] protocol = "grpc" request_types = ["inserts"] ratio = 0.3 ``` The above configuration formulates two sampling rules and sets a default sampling rate. GreptimeDB will start matching from the first one according to the sampling rules, and use the first matching sampling rule as the sampling rate of the tracing. If no rule matches, `default_ratio` will be used as the default sampling rate. The range of sampling rate is `[0.0, 1.0]`, `0.0` means not sampling, `1.0` means sampling all tracing. For example, according to the rules provided above, all calls accessed using the mysql protocol will be sampled, data inserted using grpc will be sampled 30%, and all remaining tracing will not be sampled. --- ## Deployments & Administration # Deployments & Administration This section covers running GreptimeDB on your own infrastructure. Start with [Architecture](/user-guide/concepts/architecture.md) to see which components you will deploy and operate. GreptimeCloud runs the same engine as a managed service, with deployment and maintenance handled for you. ## Deploy - [Configuration](configuration.md) — protocol, storage, and runtime settings to review before the first deployment. - [Deploy on Kubernetes](./deploy-on-kubernetes/overview.md) — deployment through the GreptimeDB Operator. - [Capacity Planning](./capacity-plan.md) — sizing compute, memory, and local cache for the expected ingestion rate and query mix. - [Authentication](./authentication/overview.md) — not enabled by default. - [Run on Android](run-on-android.md) — for edge deployments on Android devices. ## Operate - [Monitoring](./monitoring/overview.md) — cluster health and performance through metrics, tracing, and runtime information. - [Table and Region Operations](./manage-data/overview.md) — table operations, sharding, Region migration and failover, repartition, compaction, and garbage collection. - [Metadata Storage](./manage-metadata/overview.md) — the metadata backend the cluster depends on. - [Maintenance](./maintenance/maintenance-mode.md) — maintenance mode, recovery mode, table reconciliation, and sequence management. - [Upgrade](./upgrade.md) — moving a deployment to a newer version. - [Troubleshooting](./troubleshooting.md) — collecting the information needed to diagnose a problem. ## Durability and recovery - [Write-Ahead Logging (WAL)](./wal/overview.md) — local and Remote WAL, including the [Remote WAL setup](./wal/remote-wal/configuration.md) that cluster failover depends on. - [Disaster Recovery](./disaster-recovery/overview.md) — backup, restore, and cross-region options. ## Performance - [Performance Tuning](./performance-tuning/performance-tuning-tips.md) — cache, query, and ingestion settings to adjust from runtime evidence. - [Design Table Schema](./performance-tuning/design-table.md) — primary keys, indexes, append-only mode, and partitioning. --- ## Design Your Table Schema The design of your table schema significantly impacts both write and query performance. Before writing data, it is crucial to understand the data types, scale, and common queries relevant to your business, then model the data accordingly. Designing a table comes down to a few decisions: - which columns form the **primary key** (data ordering and time-series identity); - whether the table is **append-only or deduplicates**, and which **merge mode** to use; - which columns to **index**; - how to lay out your data across tables (**one wide table vs. multiple tables**) and **how to partition** them for scale. This guide first explains how GreptimeDB stores and reads data, then walks through each of these decisions in turn. ## Basic Concepts Before proceeding, please review the GreptimeDB [Data Model Documentation](/user-guide/concepts/data-model.md). ### Cardinality **Cardinality**: Refers to the number of unique values in a dataset. It can be classified as "high cardinality" or "low cardinality": - **Low Cardinality**: Low cardinality columns usually have constant values. The total number of unique values usually no more than 10 thousand. For example, `namespace`, `cluster`, `http_method` are usually low cardinality. - **High Cardinality**: High cardinality columns contain a large number of unique values. For example, `trace_id`, `span_id`, `user_id`, `uri`, `ip`, `uuid`, `request_id`, table auto increment id, timestamps are usually high cardinality. ### Semantic Types In GreptimeDB, columns are categorized into three semantic types: `Tag`, `Field`, and `Timestamp`. The timestamp usually represents the time of data sampling or the occurrence time of logs/events. GreptimeDB uses the `TIME INDEX` constraint to identify the `Timestamp` column. So the `Timestamp` column is also called the `TIME INDEX` column. If you have multiple columns with timestamp data type, you can only define one as `TIME INDEX` and others as `Field` columns. In GreptimeDB, tag columns are optional. GreptimeDB reuses the `PRIMARY KEY` constraint to define tag columns; together they identify a time series and define how rows are ordered in storage (see [How GreptimeDB Stores and Reads Data](#how-greptimedb-stores-and-reads-data)). The main purposes of tag columns are: 1. Defining the storage ordering of data, which improves the locality of data with the same tags. If there are no tag columns, GreptimeDB sorts rows by timestamp. 2. Identifying a unique time series, so GreptimeDB can deduplicate rows under the same time series (primary key) when the table is not append-only. 3. Smoothing migration from other TSDBs that use tags or labels. ## How GreptimeDB Stores and Reads Data Understanding how GreptimeDB stores data and executes a query makes the rest of this guide easier to follow. Later sections refer back to the ideas introduced here. For the engine-level details — the on-disk SST file format and the full pruning pipeline — see [Data Layout in SST Files](/contributor-guide/datanode/storage-engine.md#data-layout-in-sst-files) and [Scan Pruning](/contributor-guide/datanode/storage-engine.md#scan-pruning) in the storage engine documentation. ### Data is sorted by primary key and time GreptimeDB stores rows sorted by `(primary key, timestamp)`. Rows that share the same primary key form a single time series and are stored next to each other, ordered by time. This locality is what makes scanning a single time series cheap and improves compression. For a concrete example of how rows are laid out on disk, see [Data Layout in SST Files](/contributor-guide/datanode/storage-engine.md#data-layout-in-sst-files). ### A scan prunes data in stages GreptimeDB stores data in immutable files (SST files). Each file is split into row groups, and each row group keeps statistics such as the minimum and maximum value of every column. When you run a query, GreptimeDB avoids reading data that cannot match, in increasingly precise stages: 1. **Time range**: skip whole files (and in-memory buffers) whose time range does not overlap the query's time range. This is usually the cheapest and most effective step for time-series data. 2. **Row-group min/max statistics**: within a file, skip row groups whose statistics prove that no row can match a filter. 3. **Index**: if a filtered column has an index, use it to further narrow down to specific row groups or rows. 4. **Read and filter**: read the remaining data and apply the exact filters. Take a query over a `host_metrics` table with `PRIMARY KEY (host, region)` as an example: ```sql SELECT ts, cpu FROM host_metrics WHERE host = 'host-a' AND region = 'us-east' AND ts >= '2024-01-01 10:00:00' AND ts < '2024-01-01 11:00:00' AND cpu > 0.7; ``` **Time range** removes any file whose data falls outside the one-hour window. **Primary-key ordering** keeps all `host-a` / `us-east` rows together, so the scan reads a small contiguous slice of each remaining file instead of the whole file. **Row-group statistics on the first primary-key column** are especially effective. Because rows are sorted by primary key, the leading primary-key column (`host`) has a tight, non-overlapping range in each row group: | Row group | host (min..max) | | --- | --- | | 0 | host-a .. host-b | | 1 | host-c .. host-f | The filter `host = 'host-a'` can only match row group 0, so row group 1 is skipped without being read. Choosing and ordering the primary key well turns the leading primary-key column into an effective coarse pruning key — no extra index required. This pruning is coarse, though: GreptimeDB keeps no global index over the primary key, so it cannot jump straight to the rows for a given key. It only skips row groups by their min/max values, which works well for the leading primary-key column but does little for columns deeper in the key or for point lookups on high cardinality columns — that is what indexes are for. Statistics on field columns help too: a row group whose `cpu` maximum is `0.6` is skipped for `cpu > 0.7`. **Indexes** handle selective filters that ordering and statistics cannot. For example, an index on a column lets the scan jump to the matching row groups or rows before decoding the data columns. See [Index](#index). ### Deduplication happens during the scan GreptimeDB uses an LSM-tree storage engine: it never overwrites data in place, so multiple versions of the same row can exist across files. For tables that are not append-only, the scan **merges and deduplicates** rows that share the same `(primary key, timestamp)` on the fly, keeping only the newest version (see [Data updating and merging](#data-updating-and-merging)). Append-only tables skip this work. They don't need to deduplicate, and when a query doesn't require ordered output they can be scanned in any order, which is faster. ### Takeaways - Filtering by **time range** and by the **leading primary-key columns** is the cheapest way to make queries fast. - **Indexes** help selective filters on columns that ordering doesn't cover. - **Deduplication** (on non-append-only tables) adds extra work at query time. ## Primary key The primary key is the most impactful schema decision: it defines how rows are ordered on disk and identifies a time series for deduplication. This section helps you decide whether to set one and which columns to use. ### Primary key is optional Bad primary key or index may significantly degrade performance. Generally you can create an append-only table without a primary key since ordering data by timestamp is sufficient for many workloads. It can also serve as a baseline. ```sql CREATE TABLE http_logs ( access_time TIMESTAMP TIME INDEX, application STRING, remote_addr STRING, http_status STRING, http_method STRING, http_refer STRING, user_agent STRING, request_id STRING, request STRING, ) with ('append_mode'='true'); ``` The `http_logs` table is an example for storing HTTP server logs. - The `'append_mode'='true'` option creates the table as an append-only table. This ensures a log doesn't override another one with the same timestamp. - The table sorts logs by time so it is efficient to search logs by time. ### Primary key design You can use primary key when there are suitable columns and one of the following conditions is met: - Most queries can benefit from the ordering. - You need to deduplicate (including delete) rows by the primary key and time index. For example, if you always only query logs of a specific application, you may set the `application` column as primary key (tag). ```sql SELECT request FROM http_logs WHERE application = 'greptimedb' AND access_time > now() - '5 minute'::INTERVAL; ``` The number of applications is usually limited. Table `http_logs_v2` uses `application` as the primary key. It sorts logs by application so querying logs under the same application is faster as it only has to scan a small number of rows. Setting tags may also reduce disk space usage as it improves the locality of data. ```sql CREATE TABLE http_logs_v2 ( access_time TIMESTAMP TIME INDEX, application STRING, remote_addr STRING, http_status STRING, http_method STRING, http_refer STRING, user_agent STRING, request_id STRING, request STRING, PRIMARY KEY(application), ) with ('append_mode'='true'); ``` A long primary key will negatively affect the insert performance and enlarge the memory footprint. It's recommended to define a primary key with no more than 5 columns. You can put high cardinality columns such as `trace_id`, `span_id`, or `user_id` into the primary key, but it is often not the best way to filter on them. Adding a high cardinality column to the primary key has a cost but limited benefit: - It makes the primary key longer, which slows inserts and uses more memory. - On deduplicating tables, deduplication by a high cardinality key is expensive. If you can tolerate duplication, use an append-only table instead. - Unlike a [skipping index](#skipping-index), it doesn't always speed up queries much. A skipping index targets the same filters with much lower overhead, so it is usually the better default. Add a high cardinality column to the primary key only when you genuinely need to order or deduplicate by it. Because the best layout depends on your data and queries, benchmark on your own dataset to decide. Recommendations for tags: - Low cardinality columns that occur in `WHERE`/`GROUP BY`/`ORDER BY` frequently. These columns usually remain constant. For example, `namespace`, `cluster`, or an AWS `region`. - No need to set all low cardinality columns as tags since this may impact the performance of ingestion and querying. - Typically use short strings and integers for tags, avoiding `FLOAT`, `DOUBLE`, `TIMESTAMP`. - For high cardinality columns such as `trace_id`, `span_id`, and `user_id`, prefer a [skipping index](#skipping-index); add them to the primary key only when you need to order or deduplicate by them. - Order primary key columns from the most frequently filtered and most selective leading column. The leading column benefits the most from ordering and row-group pruning (see [How GreptimeDB Stores and Reads Data](#how-greptimedb-stores-and-reads-data)). ## Deduplication and Append-Only Next, decide how the table handles rows that share the same primary key and timestamp: - **Append-only** (`append_mode = 'true'`): keep every row, with no deduplication or deletes. This is the fastest option. - **Deduplicating** (the default): keep a single row per `(primary key, timestamp)`, with a **merge mode** (`last_row` or `last_non_null`) controlling how updates combine. Choose append-only when you don't need updates or deletes (for example, logs). Otherwise, use the default deduplicating mode, which keeps `append_mode` at `false` and removes duplicate rows by primary key and timestamp. Because deduplication groups rows by `(primary key, timestamp)`, the primary key decides what counts as a duplicate. Make sure the primary key uniquely identifies a time series, so that rows you want to keep separate are not merged together (see [Primary key](#primary-key)). For example, the `system_metrics` table deduplicates rows by `host` and `ts`: ```sql CREATE TABLE IF NOT EXISTS system_metrics ( host STRING, cpu_util DOUBLE, memory_util DOUBLE, disk_util DOUBLE, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(host), TIME INDEX(ts) ); ``` ### Data updating and merging GreptimeDB supports two different strategies for deduplication: `last_row` and `last_non_null`. You can specify the strategy by the `merge_mode` table option. `merge_mode` only takes effect on deduplicating tables. Append-only tables don't merge rows, so you normally leave `merge_mode` unset on them. GreptimeDB uses an LSM Tree-based storage engine, which does not overwrite old data in place but allows multiple versions of data to coexist. These versions are merged during the query process. The default merge behavior is `last_row`, meaning the most recently inserted row takes precedence. ![merge-mode-last-row](/merge-mode-last-row.png) In `last_row` merge mode, the latest row is returned for queries with the same primary key and time value, requiring all Field values to be provided during updates. For scenarios where only specific Field values need updating while others remain unchanged, the `merge_mode` option can be set to `last_non_null`. This mode retains the latest non-null value for each field during queries, allowing updates to provide only the values that need to change. ![merge-mode-last-non-null](/merge-mode-last-non-null.png) Tables auto-created via the InfluxDB line protocol default to `last_non_null` to align with InfluxDB's update behavior, configurable via the [`influxdb.default_merge_mode`](/user-guide/deployments-administration/configuration.md) option. The `last_row` merge mode doesn't have to check each individual field value so it is usually faster than the `last_non_null` mode. :::warning Deduplication and partitioning Deduplication and merging happen **within a single partition**. If you partition a deduplicating table (any table that is not append-only) by a column that is **not** part of the primary key, rows with the same primary key can be spread across different partitions and won't be deduplicated against each other. To keep deduplication correct, only use primary key columns as partition columns, so that rows with the same primary key always go to the same partition. GreptimeDB does not enforce this — it is your responsibility. See [Distributed Tables](#distributed-tables). ::: ### When to use append-only tables If you don't need the following features, you can use append-only tables: - Deduplication - Deletion GreptimeDB implements `DELETE` via deduplicating rows so append-only tables don't support deletion now. Deduplication requires more computation and restricts the parallelism of ingestion and querying. Using append-only tables usually has better query performance. ## Index With the primary key and table mode decided, you can add indexes to speed up specific filters. Indexes are optional — add one only when a filter is common and not already fast enough, and choose the index type based on the column (see the comparison in [When to use index](#when-to-use-index)). A primary key and an index are complementary, not alternatives: - The **primary key** gives every table one physical ordering. It helps range scans and locality, and is required for deduplication and deletion. - An **index** is auxiliary and can be added to any column, whether a tag or a field. It targets selective filters (for example a point lookup on a high cardinality column) that ordering alone can't accelerate. A single query can use both at once. For example, with a primary key on `application` and a [skipping index](#skipping-index) on `request_id`, GreptimeDB uses the time range and the `application` ordering to read a small slice of data, then uses the index on `request_id` to pinpoint the matching rows. ### Inverted Index GreptimeDB supports inverted index that may speed up filtering low cardinality columns. When creating a table, you can specify the [inverted index](/contributor-guide/datanode/data-persistence-indexing.md#inverted-index) columns using the `INVERTED INDEX` clause. For example, `http_logs_v3` adds an inverted index for the `http_method` column. ```sql CREATE TABLE http_logs_v3 ( access_time TIMESTAMP TIME INDEX, application STRING, remote_addr STRING, http_status STRING, http_method STRING INVERTED INDEX, http_refer STRING, user_agent STRING, request_id STRING, request STRING, PRIMARY KEY(application), ) with ('append_mode'='true'); ``` The following query can use the inverted index on the `http_method` column. ```sql SELECT request FROM http_logs_v3 WHERE application = 'greptimedb' AND http_method = 'GET' AND access_time > now() - '5 minute'::INTERVAL; ``` Inverted index supports the following operators: - `=` - `<` - `<=` - `>` - `>=` - `IN` - `BETWEEN` - `~` ### Skipping Index For high cardinality columns like `trace_id`, `request_id`, a [skipping index](/user-guide/manage-data/data-index.md#skipping-index) is often a good fit. This method has lower storage overhead and resource usage, particularly in terms of memory and disk consumption. Example: ```sql CREATE TABLE http_logs_v4 ( access_time TIMESTAMP TIME INDEX, application STRING, remote_addr STRING, http_status STRING, http_method STRING INVERTED INDEX, http_refer STRING, user_agent STRING, request_id STRING SKIPPING INDEX, request STRING, PRIMARY KEY(application), ) with ('append_mode'='true'); ``` The following query can use the skipping index to filter the `request_id` column. ```sql SELECT request FROM http_logs_v4 WHERE application = 'greptimedb' AND request_id = '25b6f398-41cf-4965-aa19-e1c63a88a7a9' AND access_time > now() - '5 minute'::INTERVAL; ``` However, note that the query capabilities of the skipping index are generally inferior to those of the inverted index. Skipping index can't handle complex filter conditions and may have a lower filtering performance on low cardinality columns. It applies to equality predicates and to predicates that reduce to a membership test, such as `IN` and a chain of `OR` equalities. Other operators do not use the index. ### Full-Text Index For unstructured log messages that require tokenization and searching by terms, GreptimeDB provides full-text index. For example, the `raw_logs` table stores unstructured logs in the `message` field. ```sql CREATE TABLE IF NOT EXISTS `raw_logs` ( message STRING NULL FULLTEXT INDEX WITH(analyzer = 'English', case_sensitive = 'false'), ts TIMESTAMP(9) NOT NULL, TIME INDEX (ts), ) with ('append_mode'='true'); ``` The `message` field is full-text indexed using the `FULLTEXT INDEX` option. See [fulltext column options](/user-guide/manage-data/data-index.md#full-text-index) for more information. Storing and querying structured logs usually have better performance than unstructured logs with full-text index. It's recommended to [use Pipeline](/user-guide/logs/use-custom-pipelines.md#create-a-custom-pipeline) to convert logs into structured logs. ### When to use index Index in GreptimeDB is flexible and powerful. You can create an index for any column, no matter if the column is a tag or a field. It's meaningless to create additional index for the timestamp column. Generally you don't need to create indexes for all columns. Maintaining indexes may introduce additional cost and stall ingestion. A bad index may occupy too much disk space and make queries slower. You can use a table without additional index as a baseline. There is no need to create an index for the table if the query performance is already acceptable. You can create an index for a column when: - The column occurs in the filter frequently. - Filtering the column without an index isn't fast enough. - There is a suitable index for the column. The following table lists the suitable scenarios of all index types. | | Inverted Index | Full-Text Index | Skip Index| | ----- | ----------- | ------------- |------------- | | Suitable Scenarios | - Filtering low cardinality columns | - Text content search | - Precise filtering high cardinality columns | | Creation Method | - Specified using `INVERTED INDEX` |- Specified using `FULLTEXT INDEX` in column options | - Specified using `SKIPPING INDEX` in column options | ## Wide Table vs. Multiple Tables With each table's schema settled, decide how to spread your data across tables: group columns collected together into one wide table, use separate tables for logically distinct data, and partition a table only when a single node can no longer serve it. In monitoring or IoT scenarios, multiple metrics are often collected simultaneously. We recommend placing metrics collected simultaneously into a single table to improve read/write throughput and data compression efficiency. ![wide_table](/wide_table.png) ### Prometheus metrics and the metric engine For Prometheus-style metrics, GreptimeDB relies on the [Metric Engine](/contributor-guide/datanode/metric-engine.md). If you ingest data through the Prometheus remote write API, GreptimeDB routes it to the metric engine automatically: tables are created for you and no configuration is needed. Under the hood, the metric engine handles Prometheus's single-value model efficiently by storing many small metric tables on a shared physical wide table. Each metric stays its own logical table, while the shared storage improves read/write throughput and compression. When you scale out to a cluster, one detail matters: partitioning. By default, the metric engine uses a single physical table with **only one partition**, which is enough for most workloads. In a cluster, however, that means a single datanode handles all ingestion. To spread the load across nodes, create your own partitioned physical table on a suitable label such as `namespace`. See [GreptimeDB cluster with metric engine](/user-guide/ingest-data/for-observability/prometheus.md#greptimedb-cluster-with-metric-engine) for an example. ### Multiple tables vs. multiple partitions Splitting data into multiple tables and partitioning a single table solve different problems and can be combined: - Use **multiple tables** when data is logically distinct: different schemas, different sets of columns, or different retention (TTL) requirements. Separate tables also let each table use its own primary key, indexes, and merge mode, whereas a single wide table has to share one of each across all its columns. They keep each schema clean and let you tune and retain each table independently. The cost is more tables: because each table maintains its own files, splitting data across many small tables produces many small files. - Use **multiple partitions** (a distributed table) when a single table grows too large for one node to serve. Partitioning splits one table's rows across nodes for horizontal scaling while keeping the data in a single table, so it spreads load without creating as many small files as many separate tables would. See [Distributed Tables](#distributed-tables). In short: split into separate tables for logical separation, and into partitions for scale. ## Distributed Tables GreptimeDB supports partitioning data tables to distribute read/write hotspots and achieve horizontal scaling. This section continues from the layout discussion above and helps you decide whether to partition a table, how many partitions to create, and which columns to partition on. ### Two misunderstandings about distributed tables At the storage layer, GreptimeDB automatically partitions data based on the TIME INDEX column. Therefore, it is unnecessary and not recommended for you to partition data by time (e.g., one partition per day or one table per week). GreptimeDB uses columnar storage, so partitioning a table refers to horizontal partitioning by rows, with each partition containing all columns. ### When to Partition and Determining the Number of Partitions A table can utilize all the resources in the machine, especially during query. Partitioning a table may not always improve the performance: - A distributed query plan isn't always as efficient as a local query plan. - Distributed query may introduce additional data transmission across the network. There is no need to partition a table unless a single machine isn't enough to serve the table. For example: - There is not enough local disk space to store the data or to cache the data when using object stores. - You need more CPU cores to improve the query performance or more memory for costly queries. - The disk throughput becomes the bottleneck. - The ingestion rate is larger than the throughput of a single node. GreptimeDB releases a [benchmark report](https://github.com/GreptimeTeam/greptimedb/tree/v1.2.1/docs/benchmarks/tsbs) with each major version update, detailing the ingestion throughput of a single partition. Use this report alongside your target scenario to estimate if the write volume approaches the single partition's limit. To estimate the total number of partitions, consider the write throughput and reserve an additional 50% resource of CPU to ensure query performance and stability. Adjust this ratio as necessary. You can reserve more CPU cores if there are more queries. ### How to choose partition columns GreptimeDB employs expressions to define partitioning rules. For optimal performance, select partition columns that are: - **Evenly distributed**: each partition should receive a similar share of data, so no single partition becomes a hotspot. The column should have enough distinct values to divide the data. - **Stable**: the value should not change for a given entity over time. - **Aligned with query conditions**: the column should appear in the filters of your common queries, so a query can be routed to a small number of partitions instead of all of them. Examples include: - Partitioning by the prefix of a trace id. - Partitioning by data center name. - Partitioning by business name. For instance, if most queries target data from a specific data center, using the data center name as a partition column is appropriate. If the data distribution is not well understood, perform aggregate queries on existing data to gather relevant information. :::warning Partition columns and deduplication If your table is **not append-only** (deduplication is enabled), the partition columns **must be a subset of the primary key columns**. Deduplication and merging only happen within a partition, so rows with the same primary key must always be routed to the same partition. Partitioning on a non-primary-key column would scatter rows with the same primary key across partitions and break deduplication. Append-only tables don't deduplicate, so they can be partitioned by any column. See [Data updating and merging](#data-updating-and-merging). ::: For more details, refer to the [table partition guide](/user-guide/deployments-administration/manage-data/table-sharding.md#partition). ## SST format This is an upgrade-only note; you don't normally need it when designing a new table. GreptimeDB stores data in SST files using the `flat` format by default. It works well across all primary key cardinalities — including high cardinality ones such as `trace_id` or `uuid` — so you are **not expected to set the `sst_format` option manually**. The only reason to set `sst_format` is when upgrading from an old version of GreptimeDB. Older versions used a legacy `primary_key` format as the default. If you upgraded from such a version, or are not sure which format a table currently uses, switch it to `flat`: ```sql ALTER TABLE http_logs_v2 SET 'sst_format' = 'flat'; ``` See [SST formats](/reference/sql/create.md#create-a-table-with-sst-format) for more details. --- ## Performance Tuning Tips A GreptimeDB instance's default configuration may not fit all use cases. It's important to tune the database configurations and usage according to the scenario. Performance tuning is not limited to server configuration. Table schema and index design directly affect ingestion and query efficiency. See [Design Your Table Schema](/user-guide/deployments-administration/performance-tuning/design-table.md) for guidance on primary keys, append-only and deduplicating tables, index selection, table layout, and partitioning. GreptimeDB provides various metrics to help monitor and troubleshoot performance issues. The official repository provides [Grafana dashboard templates](https://github.com/GreptimeTeam/greptimedb/tree/main/grafana) for both standalone and cluster modes. ## Query ### Analyze query performance To debug a slow query, run it with `EXPLAIN ANALYZE VERBOSE`: ```sql EXPLAIN ANALYZE VERBOSE ; ``` `EXPLAIN ANALYZE` executes the query and collects runtime metrics from each execution stage. The `VERBOSE` option adds scan-level metrics that are useful for identifying why the query is slow and what can improve it. See [EXPLAIN](/reference/sql/explain.md) for the meaning of the metrics in the output. When comparing repeated runs, remember that cache state may affect the timing and result of the second run. If you want to avoid measuring a fully cached repeat, change the query condition slightly while keeping the query shape equivalent, for example by shifting the time range. If the cause is still unclear, include the full `EXPLAIN ANALYZE VERBOSE` output when creating an issue. Common findings: - **Many small files to search**: A high `num_file_ranges` value, especially when many files contain less than one full row group, can mean the scan has to open many small SST files. This often comes from backfilling many different time windows or from high compaction pressure. Consider adjusting `compaction.twcs.time_window`, reviewing the write or backfill pattern so rows fill time windows more naturally, and checking compaction status. Small files do not always hurt performance if the number of files is not large. - **Many rows filtered after scanning**: If `scan_cost` is high and `rows_precise_filtered` is also high, the scan reads many candidate rows and then removes them with exact filters. See [Add indexes to improve filtering](#add-indexes-to-improve-filtering). - **Local cache misses during data fetch**: If `fetch_metrics.cache_miss`, `fetch_metrics.pages_to_fetch_store`, or `fetch_metrics.store_fetch_elapsed` is high, GreptimeDB is reading page data from object storage instead of local caches. Check the cache metrics below and consider increasing `write_cache_size` or `page_cache_size`. - **High scan preparation cost**: High `build_parts_cost` or `build_reader_cost` means GreptimeDB spends significant time building scan ranges or readers. Correlate this with `num_file_ranges`, metadata cache misses, and cache pressure. - **High metadata load time**: If `metadata_cache_metrics.metadata_load_cost` or `metadata_cache_metrics.cache_miss` is high, the SST metadata cache may be too small. Check `greptime_mito_cache_bytes{type="sst_meta"}` and consider increasing `sst_meta_cache_size`. ### Add indexes to improve filtering Indexes can reduce the amount of data that GreptimeDB reads and filters. If `EXPLAIN ANALYZE VERBOSE` shows high `scan_cost` and many `rows_precise_filtered`, compare the query's `WHERE` predicates with the table schema. Columns that occur frequently in selective filters and are not already covered by the leading primary key columns are good index candidates. Choose the index type based on the query and the column: - Use an inverted index for filters on low-cardinality columns, including equality, range, and `IN` predicates. - Use a skipping index for equality filters on high-cardinality columns such as trace IDs and request IDs. - Use a full-text index for searches over unstructured text. Indexes have storage and memory costs and can increase flush and compaction latency, so avoid indexing every column. See [Index in the table design guide](/user-guide/deployments-administration/performance-tuning/design-table.md#index) for how indexes complement primary key ordering and how to select an index type. See [Data Index](/user-guide/manage-data/data-index.md) for index syntax and management. After adding an index, rerun the same query with `EXPLAIN ANALYZE VERBOSE` and compare `scan_cost` and `rows_precise_filtered`. Index pruning also appears in the `rg_*_filtered` and `rows_*_filtered` counters for the corresponding index type. Currently, a newly added index only takes effect for newly flushed data: the index is built for SST files produced by subsequent flushes, but is not added to existing SST files. ### Metrics The following metrics help diagnose query performance issues: | Metric | Type | Description | |---|---|---| | greptime_mito_read_stage_elapsed_bucket | histogram | The elapsed time of different phases of a query in the storage engine. | | greptime_mito_cache_bytes | gauge | Size of cached contents. The `type` label indicates the cache type. | | greptime_mito_cache_hit | counter | Total count of cache hit. The `type` label indicates the cache type. | | greptime_mito_cache_miss | counter | Total count of cache miss. The `type` label indicates the cache type. | | greptime_mito_cache_eviction | counter | Total count of cache eviction. The `type` label indicates the cache type. | ### Enlarging cache size Use `greptime_mito_cache_bytes{type="..."}` as the primary signal for cache pressure. If the metric is often close to the configured capacity of the corresponding cache, consider increasing that cache size. If the metric stays far below capacity, consider reducing that cache size to free memory or disk space for other caches. Use `greptime_mito_cache_hit`, `greptime_mito_cache_miss`, and `greptime_mito_cache_eviction` as supporting signals to judge whether resizing a cache is useful. The following example lists the main cache size configurations. Some default cache sizes are adjusted according to system memory. See [Configuration](/user-guide/deployments-administration/configuration.md#region-engine-options) for the default values of these options. ```toml [[region_engine]] [region_engine.mito] # Cache size for the write cache. The `type` label value is `file` for data files. write_cache_size = "10G" # Percentage of write cache capacity allocated to index (puffin) files. # The `type` label value for this cache is `index`. index_cache_percent = 20 # Cache size for SST metadata. The `type` label value for this cache is `sst_meta`. sst_meta_cache_size = "128MB" # Cache size for vectors and arrow arrays. The `type` label value for this cache is `vector`. vector_cache_size = "512MB" # Cache size for pages of SST row groups. The `type` label value for this cache is `page`. page_cache_size = "512MB" # Cache size for time series selector (e.g. `last_value()`). The `type` label value for this cache is `selector_result`. selector_result_cache_size = "512MB" # Cache size for flat range scan results. The `type` label value for this cache is `range_result`. range_result_cache_size = "512MB" # Cache size for prefilter results. The `type` label value for this cache is `prefilter_result`. prefilter_result_cache_size = "128MB" # Cache size for manifest files. The `type` label value for this cache is `manifest`. manifest_cache_size = "256MB" [region_engine.mito.index] # Cache size for index metadata. The `type` label value for this cache is `index_metadata`. metadata_cache_size = "64MiB" # Cache size for index content. The `type` label value for this cache is `index_content`. content_cache_size = "128MiB" # Page size for the index content cache. content_cache_page_size = "64KiB" # Cache size for index query results. The `type` label value for this cache is `index_result`. result_cache_size = "128MiB" # The max capacity of the index staging directory. The `type` label value is `index_staging`. staging_size = "2GB" ``` Some tips: - 1/10 of disk space for the write cache at least. It's recommended to use a large write cache when using object storage. - The write cache is split between data files and index (puffin) files by `index_cache_percent`. If `greptime_mito_cache_bytes{type="index"}` is full but `greptime_mito_cache_bytes{type="file"}` is not, consider increasing `index_cache_percent`. - If a cache is consistently small compared with its configured capacity, reduce that cache size and allocate the resource to caches with higher pressure. - 1/4 of total memory for the `page_cache_size` at least if the memory usage is under 20% - Double the cache size if the cache hit ratio is less than 50% - Tune `index.staging_size` only for index search workloads where `greptime_mito_cache_bytes{type="index_staging"}` approaches capacity, or staging misses and evictions show pressure. It is not necessary to enlarge this setting for every deployment. ### SST format GreptimeDB stores data in SST files using the `flat` format by default. It works well across all primary key cardinalities, including high cardinality ones such as `trace_id` or `uuid`, so you are not expected to set the `sst_format` option manually. For tables with high cardinality primary keys, also consider using an [append-only table](/reference/sql/create.md#create-an-append-only-table) to further improve performance. The only reason to set `sst_format` is when upgrading from an old version of GreptimeDB, whose default was a legacy `primary_key` format. In that case, switch such tables to `flat`. See the [SST format](/user-guide/deployments-administration/performance-tuning/design-table.md#sst-format) guide for how to switch the format on existing tables, and [SST formats](/reference/sql/create.md#create-a-table-with-sst-format) for the reference. ### Using append-only table if possible In general, append-only tables have a higher scan performance as the storage engine can skip merging and deduplication. What's more, the query engine can use statistics to speed up some queries if the table is append-only. We recommend enabling the [append_mode](/reference/sql/create.md#create-an-append-only-table) for the table if it doesn't require deduplication or performance is prioritized over deduplication. For example, a log table should be append-only as log messages may have the same timestamp. ### Disable Write-Ahead-Log(WAL) If you are consuming and writing to GreptimeDB from replayable data sources such as Kafka, you can further improve write throughput by disabling WAL. Please note that when WAL is disabled, unflushed data to disk or object storage will not be recoverable and will need to be restored from the original data source, such as re-reading from Kafka or re-fetching logs. Disable WAL by setting the table option `skip_wal='true'`: ```sql CREATE TABLE logs( message STRING, ts TIMESTAMP TIME INDEX ) WITH (skip_wal = 'true'); ``` ## Ingestion ### Batching rows Batching means sending multiple rows to the database over the same request. This can significantly improve ingestion throughput. A recommended starting point is 1000 rows per batch. You can enlarge the batch size if latency and resource usage are still acceptable. ### Writing by time window Although GreptimeDB can handle out-of-order data, it still affects performance. GreptimeDB infers a time window size from ingested data and partitions the data into multiple time windows according to their timestamps. If the written rows are not within the same time window, GreptimeDB needs to split them, which affects write performance. Generally, real-time data doesn't have the issues mentioned above as they always use the latest timestamp. If you need to import data with a long time range into the database, we recommend creating the table in advance and [specifying the compaction.twcs.time_window option](/reference/sql/create.md#create-a-table-with-custom-compaction-options). ### Metrics The following metrics help diagnose ingestion issues. Most of these metrics are available in the official Grafana dashboards. Use the metric names below when you need custom PromQL or deeper investigation. | Metric | Type | Description | | -------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------- | | greptime_table_operator_ingest_rows | counter | The number of rows ingested by the table operator. Use the rate of this metric to track total write load. | | greptime_servers_http_requests_elapsed_bucket | histogram | HTTP request latency. Use labels such as `path`, `method`, and `code` to find write-related latency. | | greptime_servers_grpc_requests_elapsed_bucket | histogram | gRPC request latency. Use labels such as `path` and `code` to find write-related latency. | | greptime_mito_handle_request_elapsed_bucket | histogram | The elapsed time of handling storage engine requests on datanodes. | | greptime_mito_write_stage_elapsed_bucket | histogram | The elapsed time of different phases of processing a write request in the storage engine. | | greptime_mito_write_buffer_bytes | gauge | The current estimated bytes allocated for the write buffer (memtables). | | greptime_mito_write_rows_total | counter | The number of rows written to the storage engine. Use it to compare write load across datanodes. | | greptime_mito_write_stalling_count | gauge | The number of write requests currently stalled in each worker. | | greptime_mito_write_stall_total | counter | The total number of write requests stalled due to high memory pressure or transient region states. | | greptime_mito_write_reject_total | counter | The number of write requests rejected due to high memory pressure. | | raft_engine_sync_log_duration_seconds_bucket | histogram | The elapsed time of flushing the WAL to the disk. | | greptime_mito_flush_requests_total | counter | The number of scheduled flush requests. | | greptime_mito_flush_elapsed | histogram | The elapsed time of flushing SST files. | | greptime_mito_flush_bytes_total | counter | The number of bytes flushed to SST files. | | greptime_mito_flush_file_total | counter | The number of SST files produced by flush jobs. | ### Check ingestion throughput and request latency Use `rate(greptime_table_operator_ingest_rows[$__rate_interval])` to observe the total ingestion rate. For protocol-level latency, check `greptime_servers_http_requests_elapsed_bucket` and `greptime_servers_grpc_requests_elapsed_bucket`. Filter by labels such as `path`, `method`, and `code` to separate write requests from health checks, metrics scraping, and query requests. If frontend protocol latency is high, compare it with datanode-side metrics such as `greptime_mito_handle_request_elapsed_bucket` and `greptime_mito_write_stage_elapsed_bucket`. This helps determine whether the bottleneck is in request handling before the storage engine or inside the datanode write path. ### Diagnose datanode write pressure When datanode writes are slow or ingestion latency is high, first check whether the write buffer is under pressure: - `greptime_mito_write_buffer_bytes` - `greptime_mito_write_stall_total` - `greptime_mito_write_stalling_count` - `greptime_mito_write_reject_total` Write stalls mean GreptimeDB is applying backpressure instead of accepting writes immediately. Stalls can happen when the global write buffer reaches `global_write_buffer_size`, when a region reaches its effective per-region stall threshold, or when a region is temporarily not writable during internal state changes. If clients receive an error like `Engine write buffer is full, rejecting write requests`, the datanode has reached either the global reject threshold controlled by `global_write_buffer_reject_size` or the per-region reject threshold controlled by `write_buffer_size` or `default_region_write_buffer_size`. When a datanode is under write pressure, check flush performance and write distribution before tuning the write buffer size. Increasing the write buffer only gives the datanode more memory headroom. It does not fix slow flushes or an unbalanced table that sends most writes to one region. ### Check flush performance If the write buffer keeps growing or writes stall frequently, check whether flush is the bottleneck. Use `greptime_mito_flush_elapsed` to inspect flush latency, and use `greptime_mito_flush_requests_total`, `greptime_mito_flush_bytes_total`, and `greptime_mito_flush_file_total` to understand flush frequency and throughput. Datanode logs also help identify slow flushes and hot regions. Search for `Successfully flush memtables`. The log line includes the region, flush reason, SST files, `total_rows`, `total_bytes`, `cost`, encoded part count, and flush metrics. You can estimate flush throughput by dividing `total_rows` or `total_bytes` by `cost`. If flush elapsed time is high, for example 30 seconds or more, or the flush throughput is lower than the ingestion speed, increasing the write buffer size usually only delays stalls or rejects. In this case, review the table partitioning and region distribution so flush work can be spread across more regions and datanodes. If flush jobs are fast, usually finishing in several seconds, but the flush request rate is high and write buffer pressure appears often, increasing the write buffer size can help reduce flush frequency. ### Check write distribution For partitioned tables, check whether writes are evenly distributed across datanodes and regions. Use `greptime_mito_write_rows_total` to compare datanode-level write rows, and `greptime_mito_handle_request_elapsed_bucket` to compare datanode request latency. You can also query [`REGION_STATISTICS`](/reference/sql/information-schema/region-statistics.md) to inspect region-level statistics such as `written_bytes_since_open` and `memtable_size`. If one datanode or one region receives most of the write load, adjust the table partitioning so the workload is spread across more regions and datanodes before increasing the write buffer size. ### Tune write buffer size If stalls or rejects are caused by write buffer pressure, and flush performance and write distribution are healthy, consider increasing `region_engine.mito.global_write_buffer_size`. This is most useful for workloads with large rows, such as logs or traces. By default, `global_write_buffer_size` is auto-sized to 1/8 of OS memory with a maximum of 1GB. Increase it gradually, for example by 2x to 4x, and watch datanode memory usage. In most cases, leave `region_engine.mito.global_write_buffer_reject_size` unset so GreptimeDB uses the default reject threshold of 2x `global_write_buffer_size`. If you need writes to fail earlier under memory pressure, set it manually to a deliberate margin, commonly 1.5x to 2x `global_write_buffer_size`, based on available datanode memory and how early you want requests to be rejected. The value must be greater than `global_write_buffer_size`; otherwise, GreptimeDB sanitizes it back to 2x. For hot-region protection, configure a per-table `write_buffer_size` or set `region_engine.mito.default_region_write_buffer_size` as the cluster default. The effective value is the stall threshold: GreptimeDB schedules a flush when mutable memtable usage reaches half the value, stalls writes at the value, and rejects writes at twice the value, while other regions on the same worker can continue accepting writes. A table-level `write_buffer_size` takes precedence over the engine default. An explicit table value of `0` disables the per-region limit, while unsetting the table option falls back to the engine default. The default value of `default_region_write_buffer_size` is `0`, which disables default per-region limits. Example: ```toml [[region_engine]] [region_engine.mito] global_write_buffer_size = "2GB" # Optional. Leave unset unless you need a custom reject margin. global_write_buffer_reject_size = "3GB" # Optional. Use 0 to disable the default per-region limit. default_region_write_buffer_size = "512MB" ``` To override the region limit for a specific table: ```sql ALTER TABLE monitor SET 'write_buffer_size'='1GB'; ``` ## Schema Schema design affects both query and ingestion performance. This section describes one optimization for metric tables. For guidance on primary keys, indexes, append-only and deduplicating tables, and partitioning, see [Design Your Table Schema](/user-guide/deployments-administration/performance-tuning/design-table.md). ### Using multiple fields While designing the schema, we recommend putting related metrics that can be collected together in the same table. This can also improve the write throughput and compression ratio. For example, the following three tables collect the CPU usage metrics. ```sql CREATE TABLE IF NOT EXISTS cpu_usage_user ( hostname STRING NULL, usage_value BIGINT NULL, ts TIMESTAMP(9) NOT NULL, TIME INDEX (ts), PRIMARY KEY (hostname) ); CREATE TABLE IF NOT EXISTS cpu_usage_system ( hostname STRING NULL, usage_value BIGINT NULL, ts TIMESTAMP(9) NOT NULL, TIME INDEX (ts), PRIMARY KEY (hostname) ); CREATE TABLE IF NOT EXISTS cpu_usage_idle ( hostname STRING NULL, usage_value BIGINT NULL, ts TIMESTAMP(9) NOT NULL, TIME INDEX (ts), PRIMARY KEY (hostname) ); ``` We can merge them into one table with three fields. ```sql CREATE TABLE IF NOT EXISTS cpu ( hostname STRING NULL, usage_user BIGINT NULL, usage_system BIGINT NULL, usage_idle BIGINT NULL, ts TIMESTAMP(9) NOT NULL, TIME INDEX (ts), PRIMARY KEY (hostname) ); ``` ## Collect profiling data When GreptimeDB has high CPU or memory usage and the cause is unclear, collect profiling data from the affected node and provide it to developers for further analysis. Profiling is platform-specific and may be unavailable on some platforms or builds. CPU profiling is available on most Unix platforms. Memory profiling is available on Linux, but not available on macOS or Windows. Generating flamegraphs can consume extra memory because GreptimeDB needs to parse the profile and symbolize stack information in process. Be careful when collecting flamegraphs from nodes that are close to their memory limit. Profiling is node-local. Replace `127.0.0.1:4000` in the following examples with the HTTP address of the GreptimeDB node that has high CPU or memory usage. ### CPU profiling Collect a 10-second CPU flamegraph while CPU usage is high: ```bash curl -X POST -s 'http://127.0.0.1:4000/debug/prof/cpu?seconds=10&output=flamegraph' > greptime-cpu.svg ``` ### Memory profiling Memory profiling requires heap profiling to be enabled. Official Docker images enable heap profiling by default. For self-built or non-Docker deployments, we recommend starting GreptimeDB with heap profiling enabled by default: ```bash MALLOC_CONF=prof:true ./greptime standalone start ``` Collect a memory flamegraph whenever memory usage is high, especially if it is still increasing or has reached a new peak: ```bash curl -X POST -s 'http://127.0.0.1:4000/debug/prof/mem?output=flamegraph' > greptime-mem.svg ``` You can also collect two memory profiles at different times and compare them to identify increased allocations: ```bash curl -X POST -s 'http://127.0.0.1:4000/debug/prof/mem?output=proto' > greptime-mem-before.pprof curl -X POST -s 'http://127.0.0.1:4000/debug/prof/mem?output=proto' > greptime-mem-after.pprof ``` See [Profiling Tools](/reference/http-endpoints.md#profiling-tools) for the endpoint reference. When reporting the issue, include: - GreptimeDB version and deployment mode. - Affected component or node. - CPU flamegraph or memory flamegraph/profile files. - Approximate time range and workload when the issue happened. - Relevant metrics and logs if available. --- ## Run on Android Platforms Since v0.4.0, GreptimeDB supports running on Android platforms with ARM64 CPU and Android API level >= 23. ## Install terminal emulator on Android You can install [Termux](https://termux.dev/) from [GitHub release page](https://github.com/termux/termux-app/releases/latest). ## Download GreptimeDB Android binary. ```bash VERSION=$(curl -sL \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "https://api.github.com/repos/GreptimeTeam/greptimedb/releases/latest" | sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p') curl -sOL "https://github.com/GreptimeTeam/greptimedb/releases/download/${VERSION}/greptime-android-arm64-${VERSION}.tar.gz" tar zxvf ./greptime-android-arm64-${VERSION}.tar.gz ./greptime -V ``` If binary's downloaded correctly, the command is expected to print the version of downloaded binary. ## Create GreptimeDB configuration file You can create a minimal configuration file that allows access from local network. ```bash DATA_DIR="$(pwd)/greptimedb-data" mkdir -p ${DATA_DIR} cat < ./config.toml [http] addr = "0.0.0.0:4000" [grpc] bind_addr = "0.0.0.0:4001" [mysql] addr = "0.0.0.0:4002" [storage] data_home = "${DATA_DIR}" type = "File" EOF ``` ## Start GreptimeDB from command line ```bash ./greptime --log-dir=$(pwd)/logs standalone start -c ./config.toml ``` ## Connect to GreptimeDB running on Android > You can find the IP address of your Android device from system settings or `ifconfig`. You can now connect to the GreptimeDB instance running on Android from another device with MySQL client installed. ```bash mysql -h -P 4002 ``` And play like on any other platforms! ``` Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 5.1.10-alpha-msql-proxy Greptime Copyright (c) 2000, 2023, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> CREATE TABLE monitor (env STRING, host STRING, ts TIMESTAMP, cpu DOUBLE DEFAULT 0, memory DOUBLE, TIME INDEX (ts), PRIMARY KEY(env,host)); Query OK, 0 rows affected (0.14 sec) mysql> INSERT INTO monitor(ts, env, host, cpu, memory) VALUES -> (1655276557000,'prod', 'host1', 66.6, 1024), -> (1655276557000,'prod', 'host2', 66.6, 1024), -> (1655276557000,'prod', 'host3', 66.6, 1024), -> (1655276558000,'prod', 'host1', 77.7, 2048), -> (1655276558000,'prod', 'host2', 77.7, 2048), -> (1655276558000,'test', 'host3', 77.7, 2048), -> (1655276559000,'test', 'host1', 88.8, 4096), -> (1655276559000,'test', 'host2', 88.8, 4096), -> (1655276559000,'test', 'host3', 88.8, 4096); Query OK, 9 rows affected (0.14 sec) mysql> mysql> select * from monitor where env='test' and host='host3'; +------+-------+---------------------+------+--------+ | env | host | ts | cpu | memory | +------+-------+---------------------+------+--------+ | test | host3 | 2022-06-15 15:02:38 | 77.7 | 2048 | | test | host3 | 2022-06-15 15:02:39 | 88.8 | 4096 | +------+-------+---------------------+------+--------+ 2 rows in set (0.20 sec) ``` --- ## Troubleshooting When encountering errors or performance issues, you can understand GreptimeDB's status through metrics and logs. This information can also help further investigate the root causes of problems. The following lists troubleshooting methods for some common abnormal situations. For cases where the cause cannot be easily identified, providing metrics and logs to the official team can also improve the efficiency of official troubleshooting. ## Checking CPU and Memory Load You can directly view the CPU and Memory load of corresponding components from the Dashboard. CPU is displayed in millicores, while Memory shows the current process RSS. You need to pay attention to whether the corresponding CPU and Memory load exceeds the Pod's Limit. If CPU has reached the Pod's Limit, throttling will be triggered, and clients will experience slower request processing. If Memory reaches over 70% of the Limit, it may result in OOM (Out of Memory). ## Flow Creation Failure When flow creation fails, one common scenario is that flownode is not deployed. You can check: - Whether flownode is deployed in the cluster - Whether the flownode status in the cluster is READY If flownode is already deployed, you can further investigate by examining metasrv and flownode logs, or check whether the flow node was successfully created through internal tables: ```sql select * from information_schema.cluster_info; ``` ## Object Storage Configuration Issues When object storage is misconfigured, GreptimeDB will encounter exceptions when accessing object storage. If GreptimeDB hasn't stored any data, it generally doesn't need to access object storage, so errors might not be observable immediately after deployment. Once you create a table or start writing data to GreptimeDB through write protocols, you can observe request errors. Typically, DB error messages will include object storage error details. You can find specific object storage error information through DB error logs. Some common error causes include: - Incorrect Access Key or Secret Access Key - Improper object storage permission configuration - If using Tencent Cloud COS's S3-compatible API, since Tencent Cloud has [disabled path-style domains](https://cloud.tencent.com/document/product/436/102489), you need to set `enable_virtual_host_style = true` in GreptimeDB's S3 configuration For S3 as an example, GreptimeDB requires the following permissions: ```txt "s3:PutObject", "s3:ListBucket", "s3:GetObject", "s3:DeleteObject" ``` ## Low Write Throughput You can observe write throughput through the Ingestion-related panels in the dashboard: ![ingestion rate](/ingestion-rate.jpg) If write throughput is lower than expected load it might be due to high write latency causing DB backlog. The dashboard provides panels in the `Frontend Requests` area to observe request latency: ![p99-latencies](/dashboard-p99-latencies.jpg) You can check if any nodes are experiencing stall through the `Write Stall` panel in the `Mito Engine` area (stall values remaining above 0 for extended periods). If so, it indicates that node writes have encountered bottlenecks. ![write-stall](/write-stall.jpg) By observing the `Write Buffer` panel, you can also monitor size changes in buffers used for writing. If buffers fill up quickly, consider proportionally increasing `global_write_buffer_size` and `global_write_buffer_reject_size`: ![write-buffer](/write-buffer.jpg) The Write Stage panel shows which stages have high write latency: ![write-stage](/write-stage.jpg) You can check background task status through Compaction/Flush related panels: - Whether frequent flush operations occur; if so, consider proportionally increasing `global_write_buffer_size` and `global_write_buffer_reject_size` - Whether there are long-running compaction and flush operations; if so, writes might be affected by these background tasks Additionally, logs related to `flush memtables` can show the latency of individual flush operations. ## High Memory Consumption During Writes During DB write operations, the total size of all memtables is estimated. You can view memtable memory usage through the `Write Buffer` panel. ![write-buffer-memtable](/write-buffer-memtable.jpg) Note that these values might be smaller than the actual allocated memory. If you notice that DB memory grows too quickly during writes, or if writes frequently encounter OOM situations, this might be related to unreasonable table schema design. A common cause is improper Primary Key design. If there are too many primary keys, it will consume excessive memory during writes, potentially causing database memory usage to become too high. In such cases, `Write Buffer` often remains at high levels. Increasing write buffer size generally won't improve this issue; you need to reduce the number of primary key columns or remove high-cardinality primary key columns. ## High Query Latency If query latency is high, you can add `EXPLAIN ANALYZE` or `EXPLAIN ANALYZE VERBOSE` before the query and re-execute it. The query results will include execution time for each stage to assist in troubleshooting. Additionally, the `Read Stage` panel can help understand query latency across different stages: ![read-stage](/read-stage.jpg) ## Cache Full To understand the size of various caches, you can check the used size of different caches on nodes through the `Cached Bytes` panel: ![cached-bytes](/cached-bytes.jpg) For nodes with high query latency, you can also determine if caches are full by checking cache sizes. ## Object Storage Latency You can view object storage operation latency through panels in the `OpenDAL` area of the dashboard. For example, the following panel shows response latency for object storage write operations: ![opendal](/opendal.jpg) If you suspect object storage operations are experiencing jitter, you can observe the related panels. --- ## Upgrade ## Overview This guide provides upgrade instructions for GreptimeDB, including compatibility information and breaking changes for each version. Before upgrading, ensure you review the relevant breaking changes for your upgrade path. For each upgrade, review the changes introduced after your current version up to and including your target version. Reviewing intermediate releases does not mean you need to install each one; follow the supported upgrade paths below. For complete version history and feature additions, see the [Release Notes](/release-notes/). ## Upgrade Paths to v1.2 ### From v1.0 or v1.1 to v1.2 If you are currently running v1.0 or v1.1, you can upgrade directly to v1.2. Review [Upgrading from v1.0 or v1.1 to v1.2](#upgrading-from-v10-or-v11-to-v12) and complete the related checks in the [Upgrade Checklist](#upgrade-checklist) before the rollout. If you are starting from v1.0, also review [Metric Engine changes in v1.1](/1.1/user-guide/deployments-administration/upgrade/#metric-engine-changes-in-v11). ### From v0.17 or Earlier If you are upgrading to v1.2 from v0.17 or an earlier release, first review the relevant v1.0 upgrade path below and then apply the [v1.2 breaking changes](#upgrading-from-v10-or-v11-to-v12). For Metric Engine, also review the [v1.0 compaction guidance](/1.0/user-guide/deployments-administration/upgrade/#metric-engine-compaction-changes-in-v10) and [Metric Engine changes in v1.1](/1.1/user-guide/deployments-administration/upgrade/#metric-engine-changes-in-v11). ## Upgrade Paths to v1.0 ### From v0.16 to v1.0 If you are currently running v0.16, you can upgrade directly to v1.0. See [Upgrading from v0.16 to v1.0](#upgrading-from-v016-to-v10) for all relevant breaking changes. ### From v0.17 to v1.0 If you are currently running v0.17, you can upgrade directly to v1.0. See [Upgrading from v0.17 to v1.0](#upgrading-from-v017-to-v10) for breaking changes. ### From Earlier Versions **Important:** This guide only covers upgrades from v0.16 and later versions. If you are running a version earlier than v0.16, you must first upgrade to v0.16 by following the upgrade documentation for your current version. Once you have successfully upgraded to v0.16, you can then use this guide to upgrade to v1.0. ## Breaking Changes by Version ### Upgrading from v1.0 or v1.1 to v1.2 #### Removed PromQL `holt_winters` **Impact:** PromQL query parsing The `holt_winters` name has been removed. It was an alias kept for backward compatibility; the function itself is unchanged and remains available as `double_exponential_smoothing`, which GreptimeDB supported before v1.2 as well. **Action Required:** - Search dashboards, recording rules, alert rules, and API callers for `holt_winters(` - Rename each call to `double_exponential_smoothing(...)`. The arguments and the results are identical: ```promql # Before v1.2 holt_winters(prom_series[10s], 0.5, 0.1) # v1.2 and later double_exponential_smoothing(prom_series[10s], 0.5, 0.1) ``` - Re-run the affected queries in a staging environment after the change #### Rejected `fill`, `fill_left`, and `fill_right` PromQL Modifiers **Impact:** PromQL compatibility tightening to avoid incorrect query plans v1.2 upgrades the PromQL parser, which makes the binary-operator modifiers `fill`, `fill_left`, and `fill_right` syntactically valid for the first time, as in `metric_a + fill(0) metric_b`. GreptimeDB does not implement the outer-join semantics they require, so it rejects them, whether used directly or nested, rather than planning them as ordinary inner joins. Earlier releases could not parse these modifiers at all, so no query that worked before v1.2 is affected. This is also unrelated to the SQL `RANGE ... FILL` clause, which is unchanged. **Action Required:** - Search PromQL expressions for `fill(`, `fill_left(`, and `fill_right(` - Rewrite any affected query so it does not depend on these modifiers #### Pipeline Integer Narrowing Now Uses `on_failure` **Impact:** Pipeline type coercion correctness When a pipeline converts an integral input or a numeric string into a declared integer type that cannot represent it, the value no longer wraps with modulo semantics. It now follows the transform's `on_failure` policy. Previously `-1` written to `uint8` was stored as `255`, and `256` written to `int8` was stored as `0`. This applies to `int8`, `int16`, `int32`, `uint8`, `uint16`, and `uint32`, and to cross-sign conversions into `int64` and `uint64`. Float-to-integer behavior is unchanged in this release. **Action Required:** - Review pipelines that write into narrow integer targets, or that may write negative values into unsigned targets - If those pipelines relied on wraparound behavior, widen the target type or configure an explicit `on_failure` policy before upgrading - Use `POST /v1/pipelines/_dryrun` or representative staging data to verify that boundary values now produce the expected error, default, or null outcome #### Local SQL File Access Is Now Sandboxed **Impact:** `COPY` and external-table workflows that read or write local files Standalone deployments now resolve local SQL file paths inside `storage.copy_root` (default `/copy`). Distributed deployments reject local-file SQL access entirely. **Action Required:** - Identify `COPY` statements and external tables that reference local paths - If you run standalone, move those files under the sandbox, set `storage.copy_root` to a dedicated directory, or switch the workflow to object storage - If you run distributed services, migrate local-file workflows to S3, OSS, GCS, or AzBlob before upgrading - Follow [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for the detailed migration procedure #### Removed `sparse_primary_key_encoding` Configuration **Impact:** Metric engine configuration cleanup GreptimeDB now always uses sparse primary key encoding for metric tables, and the `sparse_primary_key_encoding` option has been removed. A configuration file that still sets the key loads without error; the key is ignored. **Action Required:** - Remove `sparse_primary_key_encoding` from the `[region_engine.metric]` block in your configuration files, Helm values, and automation templates. Drop the older `experimental_sparse_primary_key_encoding` key as well if it is still present. - If you previously set `sparse_primary_key_encoding = false`, note that v1.2 no longer provides an opt-out - Restart a staging environment with the cleaned configuration to confirm your deployment no longer depends on the removed setting ### Upgrading from v1.0 to v1.1 #### OSS RRSA credential precedence {#oss-rrsa-credential-precedence} **Impact:** OSS authentication can use a different RAM role after an upgrade. When upgrading from v1.0.2 to v1.1.0, ACK deployments using RRSA/OIDC can select ECS node RAM Role credentials before OIDC credentials if the Pod can obtain credentials from node metadata. If the node role lacks the required OSS permissions, table creation or writes can fail with `403 PermissionDenied` (for example, while writing a manifest). The same credential order is present in v1.1.4, v1.2.1, and `v1.3.0-alpha.1-nightly-20260907`; also review this guidance when upgrading directly from v1.0 to one of those versions. **Action Required:** 1. If the deployment uses RRSA/OIDC, set `ALIBABA_CLOUD_ECS_METADATA_DISABLED=true` in the environment of the standalone instance or all datanodes accessing OSS before rolling out the upgrade. Keep the existing RRSA environment variables and token mount. See [OSS RRSA/OIDC configuration](/user-guide/deployments-administration/configuration.md#alibaba-cloud-oss-authentication-with-rrsaoidc) for the container configuration example and credential precedence details. 2. Restart the affected instances so the setting takes effect. Do not apply it to deployments that rely on ECS RAM Role credentials. 3. Test the upgrade in a non-production environment. Verify table creation, writes, and reads, and check that OSS operations no longer return permission errors. If errors persist, check the selected identity and its bucket permissions; a `403` alone does not identify this credential-order issue. ### Upgrading from v0.17 to v1.0 For Metric Engine tables, review the [compaction guidance](/1.0/user-guide/deployments-administration/upgrade/#metric-engine-compaction-changes-in-v10) before upgrading from v0.17 to v1.0 or later. #### Jaeger HTTP Header Removal **Impact:** HTTP header deprecation The HTTP header `x-greptime-jaeger-time-range-for-operations` has been deprecated and removed. **Action Required:** - If you configured this header in your Jaeger data source or proxy, remove it from your configuration - The header will no longer have any effect #### Metric Engine Default Sparse Primary Key Encoding **Impact:** Default configuration change with performance improvements Metric Engine now enables **sparse primary key encoding** by default to improve storage efficiency and query performance for metric scenarios. **Configuration Changes:** - **NEW default:** `sparse_primary_key_encoding = true` - **DEPRECATED:** `experimental_sparse_primary_key_encoding` (use `sparse_primary_key_encoding` instead) **Action Required:** - This change does not cause data format compatibility issues - All metric tables will automatically use sparse encoding by default - If you want to continue using the old encoding method, explicitly set: ```toml [[region_engine]] [region_engine.metric] sparse_primary_key_encoding = false ``` #### `greptime_identity` Pipeline JSON Behavior Change **Impact:** JSON processing logic change The JSON processing logic in `greptime_identity` pipeline has changed significantly: **New Behavior:** - Nested JSON objects are automatically flattened into separate columns using dot notation (e.g., `object.a`, `object.b`) - Arrays are stored as JSON strings instead of JSON objects - The `flatten_json_object` parameter has been removed - A new `max_nested_levels` parameter controls flattening depth (default: 10 levels) - When the depth limit is exceeded, remaining nested structures are serialized as JSON strings **Action Required:** 1. Review your pipeline configurations that use `greptime_identity` 2. Remove any usage of the deprecated `flatten_json_object` parameter 3. Adjust queries that reference nested JSON fields to use the new dot notation 4. If you have deeply nested JSON (>10 levels), consider setting `max_nested_levels` appropriately **Example:** Before (v0.17): ```json { "user": { "name": "Alice", "age": 30 } } ``` Stored as a single JSON column. After (v1.0): ``` user.name = "Alice" user.age = 30 ``` Stored as separate columns. #### Metric Engine TSID Generation Algorithm Change **Impact:** Time Series ID generation optimization with query implications The TSID (Time Series ID) generation algorithm has been optimized by replacing `mur3::Hasher128` with the higher-performance `fxhash::FxHasher`, including a fast-path for series without NULL labels. **Performance Improvements:** - Regular scenarios: 5-6x faster - Scenarios with NULL labels: ~2.5x faster **Breaking Change Impact:** This is a **breaking change** that affects time series identification: - **Before upgrade (time < t):** Data uses the old algorithm to generate TSIDs - **After upgrade (time > t):** Data uses the new algorithm to generate TSIDs **Query Behavior:** - Queries with time ranges that **span the upgrade time `t`** may experience slight discrepancies in time series matching near time `t` - Queries with time ranges that **do not include `t`** are not affected **Action Required:** Choose one of the following upgrade strategies: 1. **Direct Upgrade (Recommended for most users):** - Accept minor query discrepancies near the upgrade time - Suitable if approximate results near the upgrade time are acceptable 2. **Export-Upgrade-Import (For zero tolerance):** - If you cannot accept any discrepancies, use this fully compatible upgrade method: 1. Export all data before upgrading 2. Upgrade to v1.0 3. Import data back into the new version - Refer to [Backup & Restore Documentation](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-data/) ### Upgrading from v0.16 to v1.0 If you are upgrading from v0.16, you need to review: 1. **All breaking changes from v0.17 to v1.0** (listed above) 2. **v0.17.0 breaking changes** (listed below) This ensures you're aware of all changes that occurred between v0.16 and v1.0. ### v0.17.0 Breaking Changes #### Ordered-Set Aggregate Functions **Impact:** SQL syntax change Ordered-set aggregate functions now require a `WITHIN GROUP (ORDER BY …)` clause. **Before:** ```sql SELECT approx_percentile_cont(latency, 0.95) FROM metrics; ``` **After:** ```sql SELECT approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) FROM metrics; ``` **Action Required:** Update all queries using ordered-set aggregate functions (`approx_percentile_cont`, `approx_percentile_cont_weight`, etc.) to include the `WITHIN GROUP (ORDER BY …)` clause. #### MySQL Protocol Comment Styling **Impact:** Comment syntax strictness Incorrect comment styling is no longer allowed in MySQL protocol. Comments must start with `--` instead of `---`. **Before:** ```sql --- This is a comment SELECT * FROM table; ``` **After:** ```sql -- This is a comment SELECT * FROM table; ``` **Action Required:** Update any SQL scripts or queries that use `---` style comments to use the standard `--` format. ## Additional v1.0 Changes (Non-Breaking) ### v1.0.0-beta.3 #### Cache Configuration Improvements The cache architecture has been refactored for better performance: **New Configuration:** - `region_engine.mito.manifest_cache_size` (default: 256MB) - specialized manifest file cache **Removed Configuration:** - `storage.cache_path` - `storage.enable_read_cache` - `storage.cache_capacity` **Action Required:** Update your configuration files to use the new `manifest_cache_size` setting and remove the deprecated storage cache options. ### v1.0.0-beta.2 #### Improved Database Compatibility - Numeric type aliases aligned with PostgreSQL and MySQL standards - Better PostgreSQL extended query support - Improved MySQL binary protocol handling **Action Required:** Test your applications to ensure compatibility with the improved behavior. ## Minimizing Business Impact During Upgrade Before upgrading GreptimeDB, it is essential to perform a comprehensive backup of your data to safeguard against potential data loss. This backup acts as a safety measure in the event of any issues during the upgrade process. ### Best Practices #### Rolling Upgrade Utilize [rolling upgrades](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/) on Kubernetes to update GreptimeDB instances incrementally. This approach replaces old instances with new ones while maintaining service availability and minimizing downtime. #### Automatic Retries Configure client applications to enable automatic retries with exponential backoff. This helps handle temporary service interruptions gracefully. #### Temporary Pause of Write Operations For applications that can tolerate brief maintenance windows, consider pausing write operations during the upgrade to ensure data consistency. #### Double Writing Implement double writing to both the old and new versions of GreptimeDB, then switch to the new version once you have verified that it is functioning correctly. This allows you to verify data consistency and gradually redirect read traffic to the upgraded version. ## Upgrade Checklist Before upgrading to your target version, complete the following checklist: ### Pre-Upgrade - [ ] If using OSS RRSA/OIDC, review the [credential precedence change](#oss-rrsa-credential-precedence) and configure the ECS metadata opt-out where needed - [ ] Review all breaking changes relevant to your upgrade path - [ ] **Backup all data and configurations** - [ ] If upgrading to v1.2, search PromQL assets for `holt_winters(`, `fill(`, `fill_left(`, and `fill_right(` - [ ] If upgrading to v1.2, identify `COPY` workflows and external tables that reference local file paths - [ ] If upgrading to v1.2, review pipelines that narrow values into `int8`/`int16`/`int32`/`uint8`/`uint16`/`uint32`, or that write negative values into unsigned targets - [ ] Identify queries using ordered-set aggregate functions (if upgrading from v0.16 or earlier) - [ ] Identify pipelines using `greptime_identity` with JSON data - [ ] Check for usage of deprecated Jaeger HTTP header (if upgrading from v0.17 or earlier) - [ ] Review metric tables if using Metric Engine - [ ] If upgrading from v0.17 to v1.0 or later, follow the [Metric Engine compaction guidance](/1.0/user-guide/deployments-administration/upgrade/#metric-engine-compaction-changes-in-v10) to choose a window and decide whether to run SWCS before upgrading - [ ] If upgrading from a version earlier than v1.1 to v1.1 or later, review [Metric Engine changes in v1.1](/1.1/user-guide/deployments-administration/upgrade/#metric-engine-changes-in-v11) ### Configuration Updates - [ ] Update configuration files (remove deprecated cache settings) - [ ] If upgrading to v1.0, update metric engine configuration if needed (`sparse_primary_key_encoding`) - [ ] If upgrading to v1.2, remove the now-ignored `sparse_primary_key_encoding` and `experimental_sparse_primary_key_encoding` overrides - [ ] Update pipeline configurations (remove `flatten_json_object`, add `max_nested_levels` if needed) - [ ] If upgrading to v1.2 standalone deployments, set `storage.copy_root` if the default sandbox path does not fit your workflow ### Code Updates - [ ] If upgrading to v1.2, rename PromQL `holt_winters(...)` calls to `double_exponential_smoothing(...)` - [ ] If upgrading to v1.2, rewrite PromQL queries that use `fill`, `fill_left`, or `fill_right` - [ ] If upgrading to v1.2, update pipelines that relied on integer wraparound and set an explicit `on_failure` policy when needed - [ ] Update SQL queries with ordered-set aggregates to use `WITHIN GROUP (ORDER BY ...)` - [ ] Update SQL scripts using `---` comments to use `--` - [ ] Update queries that access nested JSON fields to use dot notation - [ ] Remove Jaeger header configuration if present ### Testing & Deployment - [ ] Test the upgrade in a non-production environment - [ ] If upgrading from v0.17 to v1.0 or later, test with historical SSTs and monitor memory usage, compaction progress, and query failures - [ ] If upgrading to v1.2, dry-run representative pipeline inputs that hit integer boundaries and verify the expected `on_failure` result - [ ] If upgrading to v1.2, validate the updated PromQL queries in staging - [ ] If upgrading to v1.2, verify local-file `COPY` and external-table workflows after moving them into the sandbox or object storage - [ ] Verify query results, especially for: - Ordered-set aggregate functions - Nested JSON data access - Metric queries (if affected by TSID change) - [ ] Plan for rolling upgrade or maintenance window - [ ] Prepare rollback plan in case of issues - [ ] Monitor system behavior after upgrade ### Special Considerations for Metric Engine Users If you cannot accept query discrepancies near the upgrade time due to TSID algorithm change: - [ ] Plan for export-upgrade-import process - [ ] Allocate sufficient time for data export and import - [ ] Refer to [Backup & Restore Documentation](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-data/) --- ## Local WAL This section describes how to configure the local WAL for GreptimeDB Datanode component. ```toml [wal] provider = "raft_engine" file_size = "128MB" purge_threshold = "1GB" purge_interval = "1m" read_batch_size = 128 sync_write = false sync_period = "5s" ``` ## Options If you are using Helm Chart to deploy GreptimeDB, you can refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) to learn how to configure the Datanode by injecting configuration files. | Configuration Option | Description | Default Value | | -------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------- | | `provider` | The provider of the WAL. Options: `raft_engine` (local file system storage), `kafka` (remote WAL storage in Kafka), or `noop` (no-op WAL provider) | `"raft_engine"` | | `dir` | The directory where to write logs | `{data_home}/wal` | | `file_size` | The size of single WAL log file | `128MB` | | `purge_threshold` | The threshold of the WAL size to trigger purging | `1GB` | | `purge_interval` | The interval to trigger purging | `1m` | | `read_batch_size` | The read batch size | `128` | | `sync_write` | Whether to call fsync when writing every log | `false` | | `sync_period` | The interval between periodic fsync operations | `5s` | ## Best practices ### Using a separate High-Performance Volume for WAL It is beneficial to configure a separate volume for the WAL (Write-Ahead Log) directory when deploying GreptimeDB. This setup allows you to: - Leverage a high-performance disk—either a dedicated physical volume or one provisioned via a custom `StorageClass`. - Isolate WAL I/O from cache file access, reducing I/O contention and enhancing overall system performance. If you are using Helm Chart to deploy GreptimeDB, you can refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) to learn how to configure a dedicated WAL volume. --- ## Noop WAL Noop WAL is a special WAL provider for emergency situations when the configured WAL provider becomes temporarily unavailable. It does not store any WAL data. ## Availability Noop WAL is **only available in cluster mode**, not in standalone mode. ## Use Cases - **Temporary WAL Unavailability**: When the WAL provider (e.g., Kafka) is temporarily unavailable, switch the Datanode to Noop WAL to keep the cluster running. - **Testing and Development**: Useful for testing scenarios where WAL persistence is not required. ## Data Loss Warning **When using Noop WAL, all unflushed data will be lost when the Datanode is shutdown or restarted.** Only use this provider temporarily when the normal WAL provider is unavailable. Not recommended for production use except in emergency situations. ## Configuration To configure Noop WAL for a Datanode: ```toml [wal] provider = "noop" ``` In a GreptimeDB cluster, WAL configuration has two parts: - **Metasrv** - Generates WAL metadata for new regions. Should be set to `raft_engine` or `kafka`. - **Datanode** - Reads and writes WAL data. Configure as `noop` when the WAL provider is unavailable. Note: Noop WAL can only be configured on Datanode, not on Metasrv. When using Noop WAL on Datanode, Metasrv continues using its configured WAL provider. ## Best Practices - Flush regions regularly using `admin flush_table()` or `admin flush_region()` to minimize data loss. - Switch back to the normal WAL provider as soon as it becomes available. --- ## GreptimeDB WAL Overview # Write-Ahead Logging (WAL) The [Write-Ahead Logging](/contributor-guide/datanode/wal.md#introduction)(WAL) in GreptimeDB persistently records every data modification to ensure no memory-cached data loss. GreptimeDB provides three WAL storage options: - **Local WAL**: Uses an embedded storage engine([raft-engine](https://github.com/tikv/raft-engine)) within the [Datanode](/user-guide/concepts/architecture.md). - **Remote WAL**: Uses [Apache Kafka](https://kafka.apache.org/) as the external(remote) WAL storage component. - **Noop WAL**: A no-op WAL provider for emergency situations when WAL becomes unavailable. Does not store any data. ## Local WAL ### Advantages - **Low latency**: The local WAL is stored within the same process as the Datanode, eliminating network overhead and providing low write latency. - **No extra components**: The WAL is co-located with the Datanode, so nothing else has to be deployed or operated. - **Zero RPO**: When deploying GreptimeDB in the cloud, you can configure persistent storage for WAL data using cloud storage services such as AWS EBS or GCP Persistent Disk. This ensures zero [Recovery Point Objective](https://en.wikipedia.org/wiki/Disaster_recovery#Recovery_Point_Objective) (RPO), meaning no data loss, even in the event of system failure. ### Disadvantages - **High RTO**: Because the WAL resides on the same node as the Datanode, the [Recovery Time Objective](https://en.wikipedia.org/wiki/Disaster_recovery#Recovery_Time_Objective) (RTO) is relatively high. After a Datanode restarts, it must replay the WAL to restore the latest data, during which time the node remains unavailable. - **Single-Point Access Limitation**: The local WAL is tightly coupled with the Datanode process and only supports a single consumer, which limits features such as region hot standby and [Region Migration](/user-guide/deployments-administration/manage-data/region-migration.md). ## Remote WAL ### Advantages - **Low RTO**: By decoupling WAL from the Datanode, [Recovery Time Objective](https://en.wikipedia.org/wiki/Disaster_recovery#Recovery_Time_Objective) (RTO) is minimized. If a Datanode crashes, the Metasrv can quickly trigger a [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md) to migrate affected regions to healthy nodes—without the need to replay WAL locally. - **Multi-Consumer Subscriptions**: Remote WAL supports multiple consumers subscribing to WAL logs simultaneously, enabling features such as region hot standby and [Region Migration](/user-guide/deployments-administration/manage-data/region-migration.md), thereby enhancing system availability and flexibility. ### Disadvantages - **External dependencies**: Remote WAL relies on an external Kafka cluster, which increases the complexity of deployment, operation, and maintenance. - **Network overhead**: Since WAL data needs to be transmitted over the network, careful planning of cluster network bandwidth is required to ensure low latency and high throughput, especially under write-heavy workloads. ## Noop WAL Noop WAL is a special WAL provider for emergency situations when the configured WAL provider becomes temporarily unavailable. It does not store any WAL data and is only available in cluster mode. For detailed configuration, see the [Noop WAL](/user-guide/deployments-administration/wal/noop-wal.md) page. ## Next steps - [Local WAL](/user-guide/deployments-administration/wal/local-wal.md) — configuring the embedded WAL. - [Remote WAL](/user-guide/deployments-administration/wal/remote-wal/configuration.md) — configuring Kafka as the WAL backend. - [Noop WAL](/user-guide/deployments-administration/wal/noop-wal.md) — the emergency no-op provider. --- ## GreptimeDB Remote WAL Configuration # Configuration The configuration of Remote WAL contains two parts: - Metasrv Configuration - Datanode Configuration If you are using Helm Chart to deploy GreptimeDB, you can refer to [Common Helm Chart Configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) to learn how to configure Remote WAL. ## Metasrv Configuration On the Metasrv side, Remote WAL is primarily responsible for managing Kafka topics and periodically pruning stale WAL data. ```toml [wal] provider = "kafka" broker_endpoints = ["kafka.kafka-cluster.svc.cluster.local:9092"] # WAL data pruning options auto_prune_interval = "30m" auto_prune_logical_delete = false # set to true if your Kafka deployment does not support DeleteRecords auto_prune_parallelism = 10 flush_trigger_size = "512MB" checkpoint_trigger_size = "128MB" # Topic creation options auto_create_topics = true num_topics = 64 # Testing value: no redundant copy. Set this from your durability policy in production. replication_factor = 1 topic_name_prefix = "greptimedb_wal_topic" create_topic_timeout = "30s" ``` ### Options | Configuration Option | Description | |----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `provider` | The WAL provider to use. Set to `"kafka"` to enable Remote WAL with Kafka. | | `broker_endpoints` | List of Kafka broker addresses to connect to. Example: `["kafka.kafka-cluster.svc:9092"]`. | | `auto_prune_interval` | How often to automatically prune (delete) stale WAL data. Specify as a duration string (e.g., `"30m"`). Set to `"0s"` to disable automatic pruning. | | `auto_prune_logical_delete` | If `true`, automatic WAL pruning only advances GreptimeDB's metadata marker and skips Kafka `DeleteRecords`. Set this to `true` for Kafka deployments that do not support `DeleteRecords`. When enabled, actual data removal from Kafka topics relies on Kafka's own retention and cleanup policies. | | `auto_prune_parallelism` | Maximum number of concurrent pruning tasks. Increasing this value may speed up pruning but will use more resources. | | `auto_create_topics` | If `true`, Metasrv will automatically create required Kafka topics. If `false`, you must manually create all topics before starting Metasrv. | | `num_topics` | Number of Kafka topics to use for WAL storage. More topics can improve scalability and performance. | | `replication_factor` | Replication factor for Kafka topics. Determines how many Kafka brokers will store copies of each topic's data. The example value `1` keeps no redundant copy and suits testing only; for production pick a value from your durability policy. It must not exceed the number of brokers. | | `topic_name_prefix` | Prefix for Kafka topic names. WAL topics will be named as `{topic_name_prefix}_{index}` (e.g., `greptimedb_wal_topic_0`). The prefix must match the regex `[a-zA-Z_:-][a-zA-Z0-9_:\-\.@#]*`. | | `flush_trigger_size` | Estimated size threshold (e.g., `"512MB"`) for triggering a flush operation in a region. Calculated as `(latest_entry_id - flushed_entry_id) * avg_record_size`. When this value exceeds `flush_trigger_size`, MetaSrv initiates a flush. Set to `"0"` to let the system automatically determine the flush trigger size. This also controls the maximum replay size from a topic during region replay; using a smaller value can help reduce region replay time during Datanode startup. | | `checkpoint_trigger_size` | Estimated size threshold (e.g., `"128MB"`) for triggering a checkpoint operation in a region. Calculated as `(latest_entry_id - last_checkpoint_entry_id) * avg_record_size`. When this value exceeds `checkpoint_trigger_size`, MetaSrv initiates a checkpoint. Set to `"0"` to let the system automatically determine the checkpoint trigger size. Using a smaller value can help reduce region replay time during Datanode startup. | | `create_topic_timeout` | The timeout for creating a Kafka topic. Default is `"30s"`. | #### Topic Setup and Kafka Permissions To ensure Remote WAL works correctly with Kafka, please check the following: - If `auto_create_topics = false`: - All required topics must be created manually **before** starting Metasrv. - Topic names must follow the pattern `{topic_name_prefix}_{index}` where `index` ranges from `0` to `{num_topics - 1}`. For example, with the default prefix `greptimedb_wal_topic` and `num_topics = 64`, you need to create topics from `greptimedb_wal_topic_0` to `greptimedb_wal_topic_63`. - Topics must be configured to support **LZ4 compression**. - The Kafka user must have the following permissions: - **Append** records to WAL topics (requires LZ4 compression support). - **Read** records from WAL topics (requires LZ4 compression support). - **Delete** records from WAL topics (not required when `auto_prune_logical_delete = true`). - **Create** topics (only required if `auto_create_topics = true`). ## Datanode Configuration The Datanode side is mainly used to write the data to the Kafka topics and read the data from the Kafka topics. ```toml [wal] provider = "kafka" broker_endpoints = ["kafka.kafka-cluster.svc:9092"] max_batch_bytes = "1MB" create_index = false overwrite_entry_start_id = true connect_timeout = "3s" timeout = "3s" ``` ### Options | Configuration Option | Description | | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `provider` | Set to "kafka" to enable Remote WAL via Kafka. | | `broker_endpoints` | List of Kafka broker addresses. | | `max_batch_bytes` | Maximum size for each Kafka producer batch. | | `create_index` | Whether to create per-region Kafka WAL indexes. The default is `false`, and the option only takes effect in distributed mode. The index can reduce Kafka reads during recovery, but enable it only after confirming significant read amplification during single-region recovery. The Datanode persists the index to object storage every `dump_index_interval` (`"60s"` by default), adding continuous object-store I/O. | | `overwrite_entry_start_id` | If true, the Datanode will skip over missing entries during WAL replay. Prevents out-of-range errors, but may hide data loss. | | `connect_timeout` | The connect timeout for Kafka client. Default is `"3s"`. | | `timeout` | The timeout for Kafka client operations. Default is `"3s"`. | #### Required Settings and Limitations :::warning IMPORTANT: Kafka Retention Policy Configuration Please configure Kafka retention policy very carefully to avoid data loss. GreptimeDB will automatically recycle unneeded WAL data, so in most cases you don't need to set the retention policy. However, if you do set it, please ensure the following: - **Size-based retention**: Typically not needed, as the database manages its own data lifecycle - **Time-based retention**: If you choose to set this, ensure it's **significantly greater than the auto-flush-interval** to prevent premature data deletion Improper retention settings can lead to data loss if WAL data is deleted before GreptimeDB has processed it. ::: - If you set `overwrite_entry_start_id = true`: - Ensure that `auto_prune_interval` is enabled in Metasrv to allow automatic WAL pruning. - Kafka topics **must not use size-based retention policies**. - If time-based retention is enabled, the retention period should be set to a value significantly greater than auto-flush-interval, preferably at least 2 times its value. - Ensure the Kafka user used by Datanode has the following permissions: - **Append** records to WAL topics (requires LZ4 compression support). - **Read** records from WAL topics (requires LZ4 compression support). - Ensure that `max_batch_bytes` does not exceed Kafka’s maximum message size (typically 1MB by default). ## Kafka Authentication Configuration Kafka authentication settings apply to both Metasrv and Datanode under the `[wal]` section. ### SASL Kafka supports several SASL mechanisms: `PLAIN`, `SCRAM-SHA-256`, and `SCRAM-SHA-512`. ```toml [wal] broker_endpoints = ["kafka.kafka-cluster.svc.cluster.local:9092"] [wal.sasl] type = "SCRAM-SHA-512" username = "user" password = "secret" ``` ### TLS You can enable TLS encryption for Kafka connections by configuring the `[wal.tls]` section. There are three common modes: #### Using System CA Certificate To use system-wide trusted CAs, enable TLS without providing any certificate paths: ```toml [wal] broker_endpoints = ["kafka.kafka-cluster.svc.cluster.local:9092"] [wal.tls] ``` #### Using Custom CA Certificate If your Kafka cluster uses a private CA, specify the server CA certificate explicitly: ```toml [wal] broker_endpoints = ["kafka.kafka-cluster.svc.cluster.local:9092"] [wal.tls] server_ca_cert_path = "/path/to/server.crt" ``` #### Using Mutual TLS (mTLS) To enable mutual authentication, provide both the client certificate and private key along with the server CA: ```toml [wal] broker_endpoints = ["kafka.kafka-cluster.svc.cluster.local:9092"] [wal.tls] server_ca_cert_path = "/path/to/server_cert" client_cert_path = "/path/to/client_cert" client_key_path = "/path/to/key" ``` --- ## Kafka When [Remote WAL](/user-guide/deployments-administration/wal/overview.md#remote-wal) is enabled, the GreptimeDB cluster writes its write-ahead log to Kafka instead of local disks. Datanodes append WAL entries to Kafka topics, and Metasrv prunes entries that are no longer needed for recovery. Until a region flushes to object storage, Kafka may hold the only durable copy of those writes. Treat it as a stateful dependency of the database, not as a transport buffer. Getting the retention policy wrong is the most common way to lose data here: Kafka deletes segments on its own schedule, and a WAL entry deleted before GreptimeDB replays it cannot be recovered. - [Deploying Kafka Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-kafka.md) — installation, verification, monitoring, and uninstallation with the Bitnami Kafka Helm chart. - [Remote WAL Configuration](/user-guide/deployments-administration/wal/remote-wal/configuration.md) — the Metasrv and Datanode `[wal]` options, plus the [required settings and limitations](/user-guide/deployments-administration/wal/remote-wal/configuration.md#required-settings-and-limitations) the Kafka cluster must satisfy. - [Configure Remote WAL](/user-guide/deployments-administration/deploy-on-kubernetes/configure-remote-wal.md) — a complete Helm chart example on Kubernetes. --- ## Continuous Aggregation The Flow engine maintains aggregates such as sums, averages, and counts over time windows, updating the sink table as new rows arrive. Queries then read the sink table instead of scanning the source table. The three examples below cover the common cases: 1. **Real-time Analytics**: downsample a high-frequency event stream into a lower resolution. For instance, compile per-millisecond log events into requests per minute, average response times, and error rates per minute. 2. **Real-time Monitoring**: aggregate a stream and use the aggregated rows as alerts, for example emitting a row when the maximum temperature in a window exceeds a threshold. 3. **Real-time Dashboard**: precompute the counters a dashboard renders, such as request counts, average response time, and error counts per minute. ## Real-time Analytics Example ### Calculate the Log Statistics This use case is to calculate the total number of logs, the minimum size, the maximum size, the average size, and the number of packets with the size greater than 550 for each status code in a 1-minute fixed window for access logs. First, create a source table `ngx_access_log` and a sink table `ngx_statistics` with following clauses: ```sql CREATE TABLE `ngx_access_log` ( `client` STRING NULL, `ua_platform` STRING NULL, `referer` STRING NULL, `method` STRING NULL, `endpoint` STRING NULL, `trace_id` STRING NULL FULLTEXT INDEX, `protocol` STRING NULL, `status` SMALLINT UNSIGNED NULL, `size` DOUBLE NULL, `agent` STRING NULL, `access_time` TIMESTAMP(3) NOT NULL, TIME INDEX (`access_time`) ) WITH( append_mode = 'true' ); ``` ```sql CREATE TABLE `ngx_statistics` ( `status` SMALLINT UNSIGNED NULL, `total_logs` BIGINT NULL, `min_size` DOUBLE NULL, `max_size` DOUBLE NULL, `avg_size` DOUBLE NULL, `high_size_count` BIGINT NULL, `time_window` TIMESTAMP time index, `update_at` TIMESTAMP NULL, PRIMARY KEY (`status`) ); ``` Then create the flow `ngx_aggregation` to aggregate a series of aggregate functions, including `count`, `min`, `max`, `avg` of the `size` column, and the number of packets with a size greater than 550. The aggregation is calculated in 1-minute fixed windows of `access_time` column and also grouped by the `status` column. A spike in `high_size_count` or `max_size` within a single window then points you at the minute to inspect. The `EXPIRE AFTER '6h'` in the following SQL ensures that the flow computation only uses source data from the last 6 hours. Data older than 6 hours in the sink table will not be modified by this flow. For more details, see [Manage Flow](manage-flow.md#expire-after). ```sql CREATE FLOW ngx_aggregation SINK TO ngx_statistics EXPIRE AFTER '6h' COMMENT 'aggregate nginx access logs' AS SELECT status, count(client) AS total_logs, min(size) as min_size, max(size) as max_size, avg(size) as avg_size, sum(case when `size` > 550 then 1 else 0 end) as high_size_count, date_bin('1 minutes'::INTERVAL, access_time) as time_window FROM ngx_access_log GROUP BY status, time_window; ``` To observe the outcome of the continuous aggregation in the `ngx_statistics` table, insert some data into the source table `ngx_access_log`. ```sql INSERT INTO ngx_access_log VALUES ('android', 'Android', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 200, 1000, 'agent', now() - INTERVAL '1' minute), ('ios', 'iOS', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 200, 500, 'agent', now() - INTERVAL '1' minute), ('android', 'Android', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 200, 600, 'agent', now()), ('ios', 'iOS', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 404, 700, 'agent', now()); ``` Then the sink table `ngx_statistics` will be incrementally updated and contain the following data: The time-window and `update_at` timestamps in these result tables are illustrative and vary with execution time. ```sql SELECT * FROM ngx_statistics; ``` ```sql +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ | status | total_logs | min_size | max_size | avg_size | high_size_count | time_window | update_at | +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ | 200 | 2 | 500 | 1000 | 750 | 1 | 2025-04-24 06:46:00 | 2025-04-24 06:47:06.680000 | | 200 | 1 | 600 | 600 | 600 | 1 | 2025-04-24 06:47:00 | 2025-04-24 06:47:06.680000 | | 404 | 1 | 700 | 700 | 700 | 1 | 2025-04-24 06:47:00 | 2025-04-24 06:47:06.680000 | +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ 3 rows in set (0.01 sec) ``` Try to insert more data into the `ngx_access_log` table: ```sql INSERT INTO ngx_access_log VALUES ('android', 'Android', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 200, 500, 'agent', now()), ('ios', 'iOS', 'referer', 'GET', '/api/v1', 'trace_id', 'HTTP', 404, 800, 'agent', now()); ``` The sink table `ngx_statistics` now has corresponding rows updated; note how `max_size`, `avg_size` and `high_size_count` are updated: ```sql SELECT * FROM ngx_statistics; ``` ```sql +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ | status | total_logs | min_size | max_size | avg_size | high_size_count | time_window | update_at | +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ | 200 | 2 | 500 | 1000 | 750 | 1 | 2025-04-24 06:46:00 | 2025-04-24 06:47:06.680000 | | 200 | 2 | 500 | 600 | 550 | 1 | 2025-04-24 06:47:00 | 2025-04-24 06:47:21.720000 | | 404 | 2 | 700 | 800 | 750 | 2 | 2025-04-24 06:47:00 | 2025-04-24 06:47:21.720000 | +--------+------------+----------+----------+----------+-----------------+---------------------+----------------------------+ 3 rows in set (0.01 sec) ``` Here is the explanation of the columns in the `ngx_statistics` table: - `status`: The status code of the HTTP response. - `total_logs`: The total number of logs with the same status code. - `min_size`: The minimum size of the packets with the same status code. - `max_size`: The maximum size of the packets with the same status code. - `avg_size`: The average size of the packets with the same status code. - `high_size_count`: The number of packets with the size greater than 550. - `time_window`: The time window of the aggregation. - `update_at`: The time when the aggregation is updated. ### Retrieve Distinct Countries by Time Window Another example of real-time analytics is to retrieve all distinct countries from the `ngx_access_log` table. You can use the following query to group countries by time window: ```sql /* source table */ CREATE TABLE ngx_access_log ( client STRING, country STRING, access_time TIMESTAMP TIME INDEX, PRIMARY KEY(client) ) WITH( append_mode = 'true' ); /* sink table */ CREATE TABLE ngx_country ( country STRING, time_window TIMESTAMP TIME INDEX, update_at TIMESTAMP, PRIMARY KEY(country) ); /* create flow task to calculate the distinct country */ CREATE FLOW calc_ngx_country SINK TO ngx_country EXPIRE AFTER '7days'::INTERVAL COMMENT 'aggregate for distinct country' AS SELECT DISTINCT country, date_bin('1 hour'::INTERVAL, access_time) as time_window FROM ngx_access_log GROUP BY country, time_window; ``` The above query puts the data from the `ngx_access_log` table into the `ngx_country` table. It calculates the distinct country for each time window. The `date_bin` function is used to group the data into one-hour intervals. The `ngx_country` table will be continuously updated with the aggregated data, providing real-time insights into the distinct countries that are accessing the system. The `EXPIRE AFTER` clause makes the Flow ignore data with `access_time` older than 7 days; see [Manage Flow](manage-flow.md#expire-after) for details. You can insert some data into the source table `ngx_access_log`: ```sql INSERT INTO ngx_access_log VALUES ('client1', 'US', now() - '2 hour'::INTERVAL), ('client2', 'US', now() - '2 hour'::INTERVAL), ('client3', 'UK', now() - '2 hour'::INTERVAL), ('client4', 'UK', now() - '1 hour'::INTERVAL), ('client5', 'CN', now() - '1 hour'::INTERVAL), ('client6', 'CN', now() - '1 hour'::INTERVAL), ('client7', 'JP', now()), ('client8', 'JP', now()), ('client9', 'KR', now()), ('client10', 'KR', now()); ``` Wait for few seconds for the Flow to write the result to the sink table and then query: ```sql select * from ngx_country; ``` ```sql +---------+---------------------+----------------------------+ | country | time_window | update_at | +---------+---------------------+----------------------------+ | CN | 2025-04-24 05:00:00 | 2025-04-24 06:55:17.217000 | | JP | 2025-04-24 06:00:00 | 2025-04-24 06:55:17.217000 | | KR | 2025-04-24 06:00:00 | 2025-04-24 06:55:17.217000 | | UK | 2025-04-24 04:00:00 | 2025-04-24 06:55:17.217000 | | UK | 2025-04-24 05:00:00 | 2025-04-24 06:55:17.217000 | | US | 2025-04-24 04:00:00 | 2025-04-24 06:55:17.217000 | +---------+---------------------+----------------------------+ 6 rows in set (0.00 sec) ``` ## Real-Time Monitoring Example Consider a usecase where you have a stream of sensor events from a network of temperature sensors that you want to monitor in real-time. The sensor events contain information such as the sensor ID, the temperature reading, the timestamp of the reading, and the location of the sensor. You want to continuously aggregate this data to provide real-time alerts when the temperature exceeds a certain threshold. Then the query for continuous aggregation would be: ```sql /* create source table */ CREATE TABLE temp_sensor_data ( sensor_id INT, loc STRING, temperature DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY(sensor_id, loc) ) WITH( append_mode = 'true' ); /* create sink table */ CREATE TABLE temp_alerts ( sensor_id INT, loc STRING, max_temp DOUBLE, time_window TIMESTAMP TIME INDEX, update_at TIMESTAMP, PRIMARY KEY(sensor_id, loc) ); CREATE FLOW temp_monitoring SINK TO temp_alerts EXPIRE AFTER '1h' AS SELECT sensor_id, loc, max(temperature) as max_temp, date_bin('10 seconds'::INTERVAL, ts) as time_window FROM temp_sensor_data GROUP BY sensor_id, loc, time_window HAVING max_temp > 100; ``` The above query continuously aggregates data from the `temp_sensor_data` table into the `temp_alerts` table. It calculates the maximum temperature reading for each sensor and location, filtering out data where the maximum temperature exceeds 100 degrees. The `temp_alerts` table will be continuously updated with the aggregated data, providing real-time alerts (in the form of new rows in the `temp_alerts` table) when the temperature exceeds the threshold. The `EXPIRE AFTER '1h'` makes flow only calculate source data with `ts` in `(now - 1h, now)` range, see [Manage Flow](manage-flow.md#expire-after) for details. Now that we have created the flow task, we can insert some data into the source table `temp_sensor_data`: ```sql INSERT INTO temp_sensor_data VALUES (1, 'room1', 98.5, now() - '10 second'::INTERVAL), (2, 'room2', 99.5, now()); ``` The table should be empty now; wait a few seconds for the Flow to update the sink table: ```sql SELECT * FROM temp_alerts; ``` ```sql Empty set (0.00 sec) ``` Now insert some data that will trigger the alert: ```sql INSERT INTO temp_sensor_data VALUES (1, 'room1', 101.5, now()), (2, 'room2', 102.5, now()); ``` Wait a few seconds for the Flow to update the sink table: ```sql SELECT * FROM temp_alerts; ``` ```sql +-----------+-------+----------+---------------------+----------------------------+ | sensor_id | loc | max_temp | time_window | update_at | +-----------+-------+----------+---------------------+----------------------------+ | 1 | room1 | 101.5 | 2025-04-24 06:58:20 | 2025-04-24 06:58:32.379000 | | 2 | room2 | 102.5 | 2025-04-24 06:58:20 | 2025-04-24 06:58:32.379000 | +-----------+-------+----------+---------------------+----------------------------+ 2 rows in set (0.01 sec) ``` ## Real-Time Dashboard Consider a usecase in which you need a bar graph that show the distribution of packet sizes for each status code to monitor the health of the system. The query for continuous aggregation would be: ```sql /* create source table */ CREATE TABLE ngx_access_log ( client STRING, stat INT, size INT, access_time TIMESTAMP TIME INDEX ) WITH( append_mode = 'true' ); /* create sink table */ CREATE TABLE ngx_distribution ( stat INT, bucket_size INT, total_logs BIGINT, time_window TIMESTAMP TIME INDEX, update_at TIMESTAMP, PRIMARY KEY(stat, bucket_size) ); /* create flow task to calculate the distribution of packet sizes for each status code */ CREATE FLOW calc_ngx_distribution SINK TO ngx_distribution EXPIRE AFTER '6h' AS SELECT stat, trunc(size, -1)::INT as bucket_size, count(client) AS total_logs, date_bin('1 minutes'::INTERVAL, access_time) as time_window FROM ngx_access_log GROUP BY stat, time_window, bucket_size; ``` The query aggregates data from the `ngx_access_log` table into the `ngx_distribution` table. It computes the total number of logs for each status code and packet size bucket (bucket size of 10, as specified by `trunc` with a second argument of -1) within each time window. The `date_bin` function groups the data into one-minute intervals. The `EXPIRE AFTER '6h'` ensures that the flow computation only uses source data from the last 6 hours. See more details in [Manage Flow](manage-flow.md#expire-after). Now that we have created the flow task, we can insert some data into the source table `ngx_access_log`: ```sql INSERT INTO ngx_access_log VALUES ('cli1', 200, 100, now()), ('cli2', 200, 104, now()), ('cli3', 200, 120, now()), ('cli4', 200, 124, now()), ('cli5', 200, 140, now()), ('cli6', 404, 144, now()), ('cli7', 404, 160, now()), ('cli8', 404, 164, now()), ('cli9', 404, 180, now()), ('cli10', 404, 184, now()); ``` Wait a few seconds for the Flow to update the sink table: ```sql SELECT * FROM ngx_distribution; ``` ```sql +------+-------------+------------+---------------------+----------------------------+ | stat | bucket_size | total_logs | time_window | update_at | +------+-------------+------------+---------------------+----------------------------+ | 200 | 100 | 2 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | | 200 | 120 | 2 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | | 200 | 140 | 1 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | | 404 | 140 | 1 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | | 404 | 160 | 2 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | | 404 | 180 | 2 | 2025-04-24 07:05:00 | 2025-04-24 07:05:56.308000 | +------+-------------+------------+---------------------+----------------------------+ 6 rows in set (0.00 sec) ``` ## Using TQL with Flow for Advanced Time-Series Analysis :::warning Experimental Feature This experimental feature may contain unexpected behavior and have its functionality change in the future. ::: A Flow query can wrap TQL (Time Query Language), which gives you rate calculations, moving averages, and other time-window operations that are awkward to express in plain SQL. ### Understanding TQL Flow Components The TQL integration with Flow provides several advantages: 1. **Time Range Specification**: The `EVAL (start_time, end_time, step)` syntax allows precise control over the evaluation window, see [TQL](/reference/sql/tql.md). 2. **Automatic Schema Generation**: GreptimeDB creates appropriate sink tables based on TQL function outputs 3. **Continuous Processing**: Combined with Flow's scheduling, TQL functions run continuously on incoming data 4. **Advanced Analytics**: Access to sophisticated time-series functions like `rate()`, `increase()`, and statistical aggregations ### Setting Up the Source Table First, let's create a source table to store HTTP request metrics: ```sql CREATE TABLE http_requests_total ( host STRING, job STRING, instance STRING, byte DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY (host, job, instance) ); ``` This table will serve as the data source for our TQL-based Flow computations. The `ts` column acts as the time index, while `byte` represents the metric values we want to analyze. ### Creating a Rate Calculation Flow Now we'll create a Flow that uses TQL to calculate the rate of `byte` over time: ```sql CREATE FLOW calc_rate SINK TO rate_reqs EVAL INTERVAL '1m' AS TQL EVAL (now() - '1m'::interval, now(), '30s') rate(http_requests_total{job="my_service"}[1m]); ``` This Flow definition includes several key components: - **EVAL INTERVAL '1m'**: Executes the Flow every minute for continuous updates - **TQL EVAL**: Specifies the time range for evaluation from 1 minute ago to now, see [TQL](/reference/sql/tql.md). - **rate()**: TQL function that calculates the rate of change - **[1m]**: Defines a 1-minute lookback window for the rate calculation ### Wrapping TQL with a CTE If you want a cleaner flow definition or need stable output column names, you can wrap `TQL EVAL` in a simple CTE inside `CREATE FLOW`: ```sql CREATE FLOW calc_rate_cte SINK TO rate_reqs_cte EVAL INTERVAL '1m' AS WITH rate_data (ts, req_rate, host, job, instance) AS ( TQL EVAL (now() - '1m'::interval, now(), '30s') rate(http_requests_total{job="my_service"}[1m]) AS req_rate ) SELECT * FROM rate_data; ``` This pattern is useful when you want to rename columns before GreptimeDB infers the sink table schema. It is especially handy for TQL expressions whose generated value column names are verbose. The supported shape is intentionally limited: - Use exactly one TQL CTE. - End the flow query with `SELECT * FROM `. - Do not add `WHERE`, joins, extra projections, or additional CTEs around it. - If you quote the CTE name, keep the same quoted form in the outer query. ### Examining the Generated Sink Table You can inspect the automatically created sink table structure: ```sql SHOW CREATE TABLE rate_reqs; ``` ```sql +-----------+-------------------------------------+ | Table | Create Table | +-----------+-------------------------------------+ | rate_reqs | CREATE TABLE IF NOT EXISTS `rate_reqs` ( `ts` TIMESTAMP(3) NOT NULL, `prom_rate(ts_range,byte,ts,Int64(60000))` DOUBLE NULL, `host` STRING NULL, `job` STRING NULL, `instance` STRING NULL, TIME INDEX (`ts`), PRIMARY KEY (`host`, `job`, `instance`) ) ENGINE=mito | +-----------+-------------------------------------+ ``` This shows how GreptimeDB automatically generates the appropriate schema for storing TQL computation results, creating a table with the same structure as the prom ql query result. ### Testing with Sample Data Let's insert some test data to see the Flow in action: ```sql INSERT INTO TABLE http_requests_total VALUES ('localhost', 'my_service', 'instance1', 100, now() - INTERVAL '2' minute), ('localhost', 'my_service', 'instance1', 200, now() - INTERVAL '1' minute), ('remotehost', 'my_service', 'instance1', 300, now() - INTERVAL '30' second), ('remotehost', 'their_service', 'instance1', 300, now() - INTERVAL '30' second), ('localhost', 'my_service', 'instance1', 400, now()); ``` This creates a simple increasing sequence of values over time, which will produce a measurable rate when processed by our TQL Flow. ### Triggering Flow Execution To manually trigger the Flow computation and see immediate results: ```sql ADMIN FLUSH_FLOW('calc_rate'); ``` This command forces the Flow to process all available data immediately, rather than waiting for the next scheduled interval. ### Verifying Results Finally, verify that the Flow has successfully processed the data: ```sql SELECT count(*) > 0 FROM rate_reqs; ``` ```sql +---------------------+ | count(*) > Int64(0) | +---------------------+ | true | +---------------------+ ``` This query confirms that the rate calculation has produced results and populated the sink table with computed rate values. You can also examine the actual computed rate values: ```sql SELECT * FROM rate_reqs; ``` ```sql +---------------------+------------------------------------------+-----------+------------+-----------+ | ts | prom_rate(ts_range,byte,ts,Int64(60000)) | host | job | instance | +---------------------+------------------------------------------+-----------+------------+-----------+ | 2025-09-01 13:14:34 | 4.166666666666666 | localhost | my_service | instance1 | | 2025-09-01 13:15:04 | 4.444444444444444 | localhost | my_service | instance1 | +---------------------+------------------------------------------+-----------+------------+-----------+ 2 rows in set (0.03 sec) ``` Note that the timestamps and exact rate values may vary depending on when you run the example, but you should see similar rate calculations based on the input data pattern. ### Cleanup When you're done experimenting, clean up the resources: ```sql DROP FLOW calc_rate; DROP FLOW calc_rate_cte; DROP TABLE http_requests_total; DROP TABLE rate_reqs; DROP TABLE rate_reqs_cte; ``` ## Next Steps - [Manage Flow](manage-flow.md): Gain insights into the mechanisms of the Flow engine and the SQL syntax for defining a Flow. - [Expressions](expressions.md): Learn about the expressions supported by the Flow engine for data transformation. --- ## Expressions ## Aggregate functions Flow supports aggregate functions that are supported by the SQL query engine and the Flow plan, such as `COUNT`, `SUM`, `MIN`, and `MAX`. Unsupported query plans fail when the Flow is created. For a detailed list, please refer to [Aggregate Functions](/reference/sql/functions/df-functions.md#aggregate-functions). ## Scalar functions Flow supports scalar functions that are supported by the SQL query engine and the Flow plan. Unsupported query plans fail when the Flow is created. See our [SQL reference](/reference/sql/functions/overview.md) for the function catalogue. Here are some commonly used scalar functions in Flow: - [`date_bin`](/reference/sql/functions/df-functions.md#date_bin): calculates time intervals and returns the start of the interval nearest to the specified timestamp. - [`date_trunc`](/reference/sql/functions/df-functions.md#date_trunc): truncate a timestamp value to a specified precision. - [`trunc`](/reference/sql/functions/df-functions.md#trunc): truncate a number to a whole number or to the specified decimal places. --- ## Manage Flows Each `flow` is a continuous aggregation query in GreptimeDB. It continuously updates the aggregated data based on the incoming data. This document describes how to create, and delete a flow. :::note `EVAL INTERVAL` schedules batching evaluations but does not select batching mode. TQL workloads require it. See [Create a flow](#create-a-flow) for execution routing and instant-TTL restrictions. ::: ## Create a Source Table Before creating a flow, you need to create a source table to store the raw data. Like this: ```sql CREATE TABLE temp_sensor_data ( sensor_id INT, loc STRING, temperature DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY(sensor_id, loc) ); ``` For new workloads, avoid `WITH ('ttl' = 'instant')` on Flow source tables. This is a legacy pattern and is not recommended for new aggregation or TQL workloads. Keep source data with an appropriate retention policy instead. ## Create a Sink Table A flow stores its aggregated data in a sink table. When the sink table does not exist, `CREATE FLOW` automatically creates it when the query result is sufficient to infer its schema. Pre-create the sink when you need control over its schema or layout, or when inference is complex. An existing sink table is validated against the flow's query result. The source and sink tables must be different tables. The sink table has to be compatible with the flow's query result: - **Column order and type**: For a pre-created SQL sink, match the query output columns in order and type. - **Time index**: Specify the `TIME INDEX` for the sink table, typically using the time window column generated by the time window function. - **Update time**: For an auto-created batching SQL sink, Flow adds an `update_at` column for the update time. TQL sinks follow the query output and do not automatically add `update_at`. A pre-created SQL sink can either match the query output width or include one extra trailing timestamp column for update time. - **Tags**: Use `PRIMARY KEY` to specify Tags, which together with the time index serve as a unique identifier for row data and optimize query performance. For example: ```sql /* Create sink table */ CREATE TABLE temp_alerts ( sensor_id INT, loc STRING, max_temp DOUBLE, time_window TIMESTAMP TIME INDEX, update_at TIMESTAMP, PRIMARY KEY(sensor_id, loc) ); CREATE FLOW temp_monitoring SINK TO temp_alerts AS SELECT sensor_id, loc, max(temperature) AS max_temp, date_bin('10 seconds'::INTERVAL, ts) AS time_window FROM temp_sensor_data GROUP BY sensor_id, loc, time_window HAVING max_temp > 100; ``` The sink table has the columns `sensor_id`, `loc`, `max_temp`, `time_window`, and `update_at`. - The first four columns correspond to the query result columns of flow: `sensor_id`, `loc`, `max(temperature)` and `date_bin('10 seconds'::INTERVAL, ts)` respectively. - The `time_window` column is specified as the `TIME INDEX` for the sink table. - The `update_at` column is the last one in the schema to store the update time of the data. - The `PRIMARY KEY` at the end of the schema definition specifies `sensor_id` and `loc` as the tag columns. This means the flow will insert or update data based on the tags `sensor_id` and `loc` along with the time index `time_window`. ## Create a flow The grammar to create a flow is: ```sql CREATE [ OR REPLACE ] FLOW [ IF NOT EXISTS ] SINK TO [ EXPIRE AFTER ] [ EVAL INTERVAL ] [ COMMENT '' ] [ WITH ( = [, ...]) ] AS ; ``` The clauses must appear in the order shown: `EXPIRE AFTER` comes before `EVAL INTERVAL`. `EVAL INTERVAL` schedules batching evaluations. TQL flows require it. SQL plans containing `Aggregate` or `Distinct` use batching, unless an instant-TTL source selects legacy streaming; ordinary projections and non-aggregate joins also use legacy streaming. Streaming ignores `EVAL INTERVAL`. For batching time-window SQL, an evaluation can be incremental rather than a full-query evaluation. Batching SQL without a usable time window requires `EVAL INTERVAL` and executes the full query; time-window aggregates can run without it. When `OR REPLACE` is specified, any existing flow with the same name will be updated to the new version. It's important to note that this only affects the flow task itself; the source and sink tables will remain unchanged. Conversely, when `IF NOT EXISTS` is specified, the command will have no effect if the flow already exists, rather than reporting an error. Additionally, please note that `OR REPLACE` cannot be used in conjunction with `IF NOT EXISTS`. - `flow-name` is a unique identifier at the catalog level. - `sink-table-name` is the table name where the materialized aggregated data is stored. It can be an existing table or a new one; see [Create a Sink Table](#create-a-sink-table) for creation and validation behavior. - `EXPIRE AFTER` is an optional interval to expire data from the Flow engine. For more details, please refer to the [`EXPIRE AFTER`](#expire-after) section. - `EVAL INTERVAL` is an optional interval for batching evaluations; streaming ignores it. - `COMMENT` is the description of the flow. - `WITH` specifies flow options. The user-facing options documented below are `defer_on_missing_source` and the experimental `experimental_enable_incremental_read`. - `SQL` part defines the continuous aggregation query. It defines the source tables that provide data for the flow. Each flow can have multiple source tables. Please refer to [Write a SQL query](#write-a-sql-query) for details. A simple example to create a flow: ```sql CREATE FLOW IF NOT EXISTS my_flow SINK TO my_sink_table EXPIRE AFTER '1 hour'::INTERVAL COMMENT 'My first flow in GreptimeDB' AS SELECT max(temperature) as max_temp, date_bin('10 seconds'::INTERVAL, ts) as time_window FROM temp_sensor_data GROUP BY time_window; ``` The created flow groups `max(temperature)` into 10-second windows and stores the result in `my_sink_table`. Data within the last hour is used in the flow. ### EXPIRE AFTER The `EXPIRE AFTER` clause specifies the interval after which data will expire from the flow engine. For a Flow with a usable time-window expression, data in the source table older than the specified interval is excluded from calculations, and older sink rows are not updated. This limits the state and recomputation range for time-window flows, including stateful queries such as those involving `GROUP BY`. Batching plans without a usable time-window expression require `EVAL INTERVAL` and execute unfiltered snapshots unless their query has a time predicate; `EXPIRE AFTER` does not add a time filter. It does not delete data from either table. If you want to delete data from the source or sink table, please [set the `TTL` option](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies) when creating tables. For example, if the flow engine processes the aggregation at 10:00:00 and the `'1 hour'::INTERVAL` is set, any input data that arrive now with a time index older than 1 hour (before 09:00:00) will expire and be ignored. Only data timestamped from 09:00:00 onwards will be used in the aggregation and to update the sink table. ### Defer creation when a source is missing By default, creating a Flow fails if one of its source tables does not exist. Set `defer_on_missing_source` to `true` to persist a pending Flow instead of failing. The Flow is not scheduled while its sources remain unresolved, and it is not activated automatically when those tables are created. `CREATE OR REPLACE` cannot activate a pending Flow. ```sql CREATE FLOW pending_flow SINK TO pending_sink WITH (defer_on_missing_source = 'true') AS SELECT * FROM source_created_later; ``` After all source tables have been created, drop and recreate the Flow to activate it. ```sql DROP FLOW pending_flow; CREATE FLOW pending_flow SINK TO pending_sink AS SELECT * FROM source_created_later; ``` ### Experimental incremental source reads :::warning Experimental feature The `experimental_enable_incremental_read` option is experimental. Its behavior and limitations may change in future releases. ::: For batching SQL flows whose source tables are append-only, you can enable incremental source reads: ```sql CREATE TABLE temp_sensor_data ( sensor_id INT, loc STRING, temperature DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY(sensor_id, loc) ) WITH ('append_mode' = 'true'); CREATE FLOW temp_monitoring SINK TO temp_alerts WITH (experimental_enable_incremental_read = 'true') AS SELECT sensor_id, loc, max(temperature) AS max_temp, date_bin('10 seconds'::INTERVAL, ts) AS time_window FROM temp_sensor_data GROUP BY sensor_id, loc, time_window; ``` When enabled, Flow attempts to read only newly appended source rows after the initial full snapshot. This is an execution optimization and does not change the query result. The optimization is not a persistence contract: the first run, and a run after a restart or when incremental reading is not safe, may use a full snapshot. The current limitations are: - All source tables must be append-only tables created with `append_mode = 'true'`. Flow creation fails if any source table is not append-only. - The optimization applies only to eligible batching SQL flows. TQL flows and plans that do not support incremental reads use the normal full-snapshot behavior. ### Write a SQL query The SQL after `AS` is planned as a standard SQL query. A typical batching time-window aggregate has this shape: ```sql SELECT AGGR_FUNCTION(column1, column2,..) [, TIME_WINDOW_FUNCTION() as time_window] FROM GROUP BY {time_window | column1, column2,.. }; ``` The query engine and Flow plan determine which SQL expressions and clauses are supported. For batching SQL flows with `Aggregate` or `Distinct` plans, `EVAL INTERVAL` schedules evaluations; TQL flows require it. Planner-valid joins, subqueries, and SQL CTEs are supported for batching SQL flows with `EVAL INTERVAL`. The query planner must still produce a valid plan; unsupported queries fail when the Flow is created. For batching time-window aggregates, `GROUP BY` commonly includes the time-window expression. See [Expressions](expressions.md) for functions commonly used in Flow queries, and [Define time window](#define-time-window) for fixed windows. Refer to [Continuous Aggregation](continuous-aggregation.md) for more examples of how to use continuous aggregation in real-time analytics, monitoring, and dashboards. ### Define time window A time window is a crucial attribute of your continuous aggregation query. It determines how data is aggregated within the flow. These time windows are left-closed and right-open intervals. A time window represents a specific range of time. Data from the source table is mapped to the corresponding window based on the time index column. The time window also defines the scope for each calculation of an aggregation expression, resulting in one row per time window in the result table. You can use `date_bin()` after the `SELECT` keyword to define fixed time windows. For example: ```sql SELECT max(temperature) as max_temp, date_bin('10 seconds'::INTERVAL, ts) as time_window FROM temp_sensor_data GROUP BY time_window; ``` In this example, the `date_bin('10 seconds'::INTERVAL, ts)` function creates 10-second time windows starting from UTC 00:00:00. The `max(temperature)` function calculates the maximum temperature value within each time window. For more details on the behavior of the function, please refer to [`date_bin`](/reference/sql/functions/df-functions.md#date_bin). :::tip NOTE The time-window expression helps Flow determine how to update results incrementally. The appropriate window size depends on the workload and query semantics. ::: ## Inspect flows Use the following commands and system tables to inspect Flow definitions and runtime information: | Command | Purpose | | --- | --- | | `SHOW FLOWS;` | List flows. | | `SHOW CREATE FLOW my_flow;` | Return a Flow definition. | | `SHOW FLOW STATUS LIKE 'my%';` | Return runtime statistics for matching flows. | | `SELECT * FROM information_schema.flows;` | View Flow definitions. | | `SELECT * FROM information_schema.flow_statistics;` | View Flow runtime statistics. | Runtime fields can initially be `NULL`, and values can lag behind the latest state in distributed deployments. See [`SHOW FLOW STATUS`](/reference/sql/show.md#show-flow-status) and [`INFORMATION_SCHEMA.FLOW_STATISTICS`](/reference/sql/information-schema/flow-statistics.md) for the returned columns. ## Flush a flow The flow engine automatically processes aggregation operations within a short period(i.e. few seconds) when new data arrives in the source table. However, you can manually trigger the flow engine to process the aggregation operation immediately using the `ADMIN FLUSH_FLOW` command. ```sql ADMIN FLUSH_FLOW('') ``` ## Delete a flow To delete a flow, use the following `DROP FLOW` clause: ```sql DROP FLOW [IF EXISTS] ``` For example: ```sql DROP FLOW IF EXISTS my_flow; ``` --- ## Flow Computation GreptimeDB's Flow engine enables real-time computation on incoming data. It is particularly beneficial for Extract-Transform-Load (ETL) processes or for performing continuous aggregations such as sum, average, and other time-window calculations. Flow materializes computation results in the sink table as source data is processed, so queries can read the computed results instead of recalculating them from raw data. Use cases include: - Keeping aggregates ready for dashboards and alerts, so queries read the sink table instead of scanning raw events. - Downsampling data points, such as using average pooling, to reduce the volume of data for storage and analysis. ## Programming Model :::note Flow uses batching mode for aggregation and TQL workloads unless an instant-TTL source selects the deprecated streaming mode. Simple non-aggregation Flow queries also use streaming mode and are not recommended for new workloads. ::: Upon data insertion into the source table, the data is made available to the Flow engine. Flow then processes the specified computation and updates the sink table with the results. Both the source and sink tables are time-series tables within GreptimeDB. Before creating a Flow, it is crucial to define the schemas for these tables and design the Flow to specify the computation logic. This process is visually represented in the following image: ![Continuous Aggregation](/flow-ani.svg) ## Quick Start Example As an example, count user agents in nginx logs. The source table is `ngx_http_log`, and the sink table is `user_agent_statistics`. First, create the source table `ngx_http_log`. To optimize performance for counting the `user_agent` field, specify it as a `TAG` column type using the `PRIMARY KEY` keyword. ```sql CREATE TABLE ngx_http_log ( ip_address STRING, http_method STRING, request STRING, status_code INT16, body_bytes_sent INT32, user_agent STRING, response_size INT32, ts TIMESTAMP TIME INDEX, PRIMARY KEY (ip_address, http_method, user_agent, status_code) ) WITH ('append_mode'='true'); ``` Next, create the sink table `user_agent_statistics`. The `update_at` column tracks the last update time of the record, which is automatically updated by the Flow engine. Although all tables in GreptimeDB are time-series tables, this computation does not require time windows. Therefore, the `__ts_placeholder` column is included as a time index placeholder. ```sql CREATE TABLE user_agent_statistics ( user_agent STRING, total_count INT64, update_at TIMESTAMP, __ts_placeholder TIMESTAMP TIME INDEX, PRIMARY KEY (user_agent) ); ``` Finally, create the Flow `user_agent_flow` to count the occurrences of each user agent in the `ngx_http_log` table. ```sql CREATE FLOW user_agent_flow SINK TO user_agent_statistics EVAL INTERVAL '1s' AS SELECT user_agent, COUNT(user_agent) AS total_count FROM ngx_http_log GROUP BY user_agent; ``` Once the Flow is created, the Flow engine will continuously process data from the `ngx_http_log` table and update the `user_agent_statistics` table with the computed results. To observe the results, insert sample data into the `ngx_http_log` table. ```sql INSERT INTO ngx_http_log VALUES ('192.168.1.1', 'GET', '/index.html', 200, 512, 'Mozilla/5.0', 1024, '2023-10-01T10:00:00Z'), ('192.168.1.2', 'POST', '/submit', 201, 256, 'curl/7.68.0', 512, '2023-10-01T10:01:00Z'), ('192.168.1.1', 'GET', '/about.html', 200, 128, 'Mozilla/5.0', 256, '2023-10-01T10:02:00Z'), ('192.168.1.3', 'GET', '/contact', 404, 64, 'curl/7.68.0', 128, '2023-10-01T10:03:00Z'); ``` After inserting the data, query the `user_agent_statistics` table to view the results. ```sql SELECT * FROM user_agent_statistics; ``` The query results will display the total count of each user agent in the `user_agent_statistics` table. ```sql +-------------+-------------+----------------------------+---------------------+ | user_agent | total_count | update_at | __ts_placeholder | +-------------+-------------+----------------------------+---------------------+ | Mozilla/5.0 | 2 | 2024-12-12 06:45:33.228000 | 1970-01-01 00:00:00 | | curl/7.68.0 | 2 | 2024-12-12 06:45:33.228000 | 1970-01-01 00:00:00 | +-------------+-------------+----------------------------+---------------------+ ``` ## Next Steps - [Continuous Aggregation](./continuous-aggregation.md): Explore the primary scenario in time-series data processing, with three common use cases for continuous aggregation. - [Manage Flow](manage-flow.md): Gain insights into the mechanisms of the Flow engine and the SQL syntax for defining a Flow. - [Expressions](expressions.md): Learn about the expressions supported by the Flow engine for data transformation. --- ## EMQX [EMQX](https://www.emqx.io/) is an open-source, highly scalable, and feature-rich MQTT broker designed for IoT and real-time messaging applications. It supports various protocols, including MQTT (3.1, 3.1.1, and 5.0), HTTP, QUIC, and WebSocket. GreptimeDB can be used as a data system for EMQX. To learn how to integrate GreptimeDB with EMQX, please refer to [Ingest MQTT Data into GreptimeDB](https://docs.emqx.com/en/emqx/latest/data-integration/data-bridge-greptimedb.html). --- ## Go GreptimeDB offers ingester libraries for high-throughput data writing. It utilizes the gRPC protocol, which supports schemaless writing and eliminates the need to create tables before writing data. For more information, refer to [Automatic Schema Generation](/user-guide/ingest-data/overview.md#automatic-schema-generation). The Go ingester SDK provided by GreptimeDB is a lightweight, concurrent-safe library that is easy to use with the metric struct. ## Quick start demos To quickly get started, you can explore the [quick start demos](https://github.com/GreptimeTeam/greptimedb-ingester-go/tree/main/examples) to understand how to use the GreptimeDB Go ingester SDK. ## Installation Use the following command to install the GreptimeDB client library for Go: ```shell go get -u github.com/GreptimeTeam/greptimedb-ingester-go@v0.7.2 ``` Import the library in your code: ```go greptime "github.com/GreptimeTeam/greptimedb-ingester-go" ingesterContext "github.com/GreptimeTeam/greptimedb-ingester-go/context" "github.com/GreptimeTeam/greptimedb-ingester-go/table" "github.com/GreptimeTeam/greptimedb-ingester-go/table/types" ) ``` ## Connect to database If you have set the [`--user-provider` configuration](/user-guide/deployments-administration/authentication/overview.md) when starting GreptimeDB, you will need to provide a username and password to connect to GreptimeDB. The following example shows how to set the username and password when using the library to connect to GreptimeDB. ```go cfg := greptime.NewConfig("127.0.0.1"). // change the database name to your database name WithDatabase("public"). // Default port 4001 // WithPort(4001). // Enable secure connection if your server is secured by TLS // WithInsecure(false). // set authentication information // If the database doesn't require authentication, just remove the WithAuth method WithAuth("username", "password") cli, _ := greptime.NewClient(cfg) defer cli.Close() ``` ## Data model Each row item in a table consists of three types of columns: `Tag`, `Timestamp`, and `Field`. For more information, see [Data Model](/user-guide/concepts/data-model.md). The types of column values could be `String`, `Float`, `Int`, `Timestamp`, `JSON` etc. For more information, see [Data Types](/reference/sql/data-types.md). ## Set table options Although the time series table is created automatically when writing data to GreptimeDB via the SDK, you can still configure table options. The SDK supports the following table options: - `auto_create_table`: Default is `True`. If set to `False`, it indicates that the table already exists and does not need automatic creation, which can improve write performance. - `ttl`, `append_mode`, `merge_mode`: For more details, refer to the [table options](/reference/sql/create.md#table-options). You can set table options using the `ingesterContext` context. For example, to set the `ttl` option, use the following code: ```go hints := []*ingesterContext.Hint{ { Key: "ttl", Value: "3d", }, } ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) ctx = ingesterContext.New(ctx, ingesterContext.WithHint(hints)) // Use the ingesterContext when writing data to GreptimeDB. // The `data` object is described in the following sections. resp, err := cli.Write(ctx, data) if err != nil { return err } ``` For how to write data to GreptimeDB, see the following sections. ## Low-level API The GreptimeDB low-level API provides a straightforward method to write data to GreptimeDB by adding rows to the table object with a predefined schema. ### Create row objects This following code snippet begins by constructing a table named `cpu_metric`, which includes columns `host`, `cpu_user`, `cpu_sys`, and `ts`. Subsequently, it inserts a single row into the table. The table consists of three types of columns: - `Tag`: The `host` column, with values of type `String`. - `Field`: The `cpu_user` and `cpu_sys` columns, with values of type `Float`. - `Timestamp`: The `ts` column, with values of type `Timestamp`. ```go // Construct the table schema for CPU metrics cpuMetric, err := table.New("cpu_metric") if err != nil { // Handle error appropriately } // Add a 'Tag' column for host identifiers cpuMetric.AddTagColumn("host", types.STRING) // Add a 'Timestamp' column for recording the time of data collection cpuMetric.AddTimestampColumn("ts", types.TIMESTAMP_MILLISECOND) // Add 'Field' columns for user and system CPU usage measurements cpuMetric.AddFieldColumn("cpu_user", types.FLOAT) cpuMetric.AddFieldColumn("cpu_sys", types.FLOAT) // Insert example data // NOTE: The arguments must be in the same order as the columns in the defined schema: host, ts, cpu_user, cpu_sys err = cpuMetric.AddRow("127.0.0.1", time.Now(), 0.1, 0.12) err = cpuMetric.AddRow("127.0.0.1", time.Now(), 0.11, 0.13) if err != nil { // Handle error appropriately } ``` To improve the efficiency of writing data, you can create multiple rows at once to write to GreptimeDB. ```go cpuMetric, err := table.New("cpu_metric") if err != nil { // Handle error appropriately } cpuMetric.AddTagColumn("host", types.STRING) cpuMetric.AddTimestampColumn("ts", types.TIMESTAMP_MILLISECOND) cpuMetric.AddFieldColumn("cpu_user", types.FLOAT) cpuMetric.AddFieldColumn("cpu_sys", types.FLOAT) err = cpuMetric.AddRow("127.0.0.1", time.Now(), 0.1, 0.12) if err != nil { // Handle error appropriately } memMetric, err := table.New("mem_metric") if err != nil { // Handle error appropriately } memMetric.AddTagColumn("host", types.STRING) memMetric.AddTimestampColumn("ts", types.TIMESTAMP_MILLISECOND) memMetric.AddFieldColumn("mem_usage", types.FLOAT) err = memMetric.AddRow("127.0.0.1", time.Now(), 112) if err != nil { // Handle error appropriately } ``` ### Insert data The following example shows how to insert rows to tables in GreptimeDB. ```go resp, err := cli.Write(ctx, cpuMetric, memMetric) if err != nil { // Handle error appropriately } log.Printf("affected rows: %d\n", resp.GetAffectedRows().GetValue()) ``` ### Streaming insert Streaming insert is useful when you want to insert a large amount of data such as importing historical data. ```go err := cli.StreamWrite(ctx, cpuMetric, memMetric) if err != nil { // Handle error appropriately } ``` Close the stream writing after all data has been written. In general, you do not need to close the stream writing when continuously writing data. ```go affected, err := cli.CloseStream(ctx) ``` ## High-level API The high-level API uses an ORM style object to write data to GreptimeDB. It allows you to create, insert, and update data in a more object-oriented way, providing developers with a friendlier experience. However, it is not as efficient as the low-level API. This is because the ORM style object may consume more resources and time when converting the objects. ### Create row objects ```go type CpuMetric struct { Host string `greptime:"tag;column:host;type:string"` CpuUser float64 `greptime:"field;column:cpu_user;type:float64"` CpuSys float64 `greptime:"field;column:cpu_sys;type:float64"` Ts time.Time `greptime:"timestamp;column:ts;type:timestamp;precision:millisecond"` } func (CpuMetric) TableName() string { return "cpu_metric" } cpuMetrics := []CpuMetric{ { Host: "127.0.0.1", CpuUser: 0.10, CpuSys: 0.12, Ts: time.Now(), } } ``` ### Insert data ```go resp, err := cli.WriteObject(ctx, cpuMetrics) log.Printf("affected rows: %d\n", resp.GetAffectedRows().GetValue()) ``` ### Streaming insert Streaming insert is useful when you want to insert a large amount of data such as importing historical data. ```go err := cli.StreamWriteObject(ctx, cpuMetrics) ``` Close the stream writing after all data has been written. In general, you do not need to close the stream writing when continuously writing data. ```go affected, err := cli.CloseStream(ctx) ``` ## Insert data in JSON type GreptimeDB supports storing complex data structures using [JSON type data](/reference/sql/data-types.md#json-type-experimental). With this ingester library, you can insert JSON data using string values. For instance, if you have a table named `sensor_readings` and wish to add a JSON column named `attributes`, refer to the following code snippet. In the [low-level API](#low-level-api), you can specify the column type as `types.JSON` using the `AddFieldColumn` method to add a JSON column. Then, use a `struct` or `map` to insert JSON data. ```go sensorReadings, err := table.New("sensor_readings") // The code for creating other columns is omitted // ... // specify the column type as JSON sensorReadings.AddFieldColumn("attributes", types.JSON) // Use struct to insert JSON data type Attributes struct { Location string `json:"location"` Action string `json:"action"` } attributes := Attributes{ Location: "factory-1" } sensorReadings.AddRow(... , attributes) // The following code for writing data is omitted // ... ``` In the [high-level API](#high-level-api), you can specify the column type as JSON using the `greptime:"field;column:details;type:json"` tag. ```go type SensorReadings struct { // The code for creating other columns is omitted // ... // specify the column type as JSON Attributes string `greptime:"field;column:details;type:json"` // ... } // Use struct to insert JSON data type Attributes struct { Location string `json:"location"` Action string `json:"action"` } attributes := Attributes{ Action: "running" } sensor := SensorReadings{ // ... Attributes: attributes, } // The following code for writing data is omitted // ... ``` For the executable code for inserting JSON data, please refer to the [example](https://github.com/GreptimeTeam/greptimedb-ingester-go/tree/main/examples/jsondata) in the SDK repository. ## Bulk Write When you need to import data in bulk and require high ingestion throughput, you can use the Bulk Write API. It is designed for batch loading scenarios, where large volumes of data are written efficiently. By using Arrow IPC encoding to aggregate multiple tables or objects on the client side and sending them in a single gRPC request, it significantly reduces network overhead and improves overall write performance compared to the regular Write API. ### Adding Data You can build data objects and add them to the bulk writer. ```go // Build the CPU metrics table cpuMetric, err := table.New("cpu_metric") if err != nil { // Handle error appropriately } cpuMetric.AddTagColumn("host", types.STRING) cpuMetric.AddTimestampColumn("ts", types.TIMESTAMP_MILLISECOND) cpuMetric.AddFieldColumn("cpu_user", types.FLOAT64) cpuMetric.AddFieldColumn("cpu_sys", types.FLOAT64) // Add multiple rows of data cpuMetric.AddRow("127.0.0.1", time.Now(), 0.1, 0.12) cpuMetric.AddRow("127.0.0.2", time.Now(), 0.2, 0.15) ``` ### Insert data ```go resp, err := cli.BulkWrite(context.TODO(), cpuMetric) if err != nil { // Handle error appropriately } log.Printf("Bulk write affected rows: %d\n", resp.GetAffectedRows().GetValue()) ``` Please refer to the [example](https://github.com/GreptimeTeam/greptimedb-ingester-go/tree/main/examples/bulkwrite) in the SDK repository for runnable code demonstrating bulk write. ## Ingester library reference - [API Documentation](https://pkg.go.dev/github.com/GreptimeTeam/greptimedb-ingester-go) --- ## Java Ingester for GreptimeDB GreptimeDB offers ingester libraries for high-throughput data writing. It utilizes the gRPC protocol, which supports schemaless writing and eliminates the need to create tables before writing data. For more information, refer to [Automatic Schema Generation](/user-guide/ingest-data/overview.md#automatic-schema-generation). The Java ingester SDK provided by GreptimeDB is a lightweight, high-performance client designed for efficient time-series data ingestion. It leverages the gRPC protocol to provide a non-blocking, purely asynchronous API that delivers exceptional throughput while maintaining seamless integration with your applications. This client offers multiple ingestion methods optimized for various performance requirements and use cases. You can select the approach that best suits your specific needs—whether you require simple unary writes for low-latency operations or high-throughput bulk streaming for maximum efficiency when handling large volumes of time-series data. ## High Level Architecture ``` +-----------------------------------+ | Client Applications | | +------------------+ | | | Application Code | | | +------------------+ | +-------------+---------------------+ | v +-------------+---------------------+ | API Layer | | +---------------+ | | | GreptimeDB | | | +---------------+ | | / \ | | v v | | +-------------+ +-------------+ | +------------------+ | | BulkWrite | | Write | | | Data Model | | | Interface | | Interface | |------->| | | +-------------+ +-------------+ | | +------------+ | +-------|----------------|----------+ | | Table | | | | | +------------+ | v v | | | +-------|----------------|----------+ | v | | Transport Layer | | +------------+ | | +-------------+ +-------------+ | | | TableSchema| | | | BulkWrite | | Write | | | +------------+ | | | Client | | Client | | +------------------+ | +-------------+ +-------------+ | | | \ / | | | | \ / | | | | v v | | | | +-------------+ | | | | |RouterClient | | | +-----|--+-------------|---+--------+ | | | | | | | | v v v | +-----|----------------|---|--------+ | Network Layer | | +-------------+ +-------------+ | | | Arrow Flight| | gRPC Client | | | | Client | | | | | +-------------+ +-------------+ | | | | | +-----|----------------|------------+ | | v v +-------------------------+ | GreptimeDB Server | +-------------------------+ ``` - **API Layer**: Provides high-level interfaces for client applications to interact with GreptimeDB - **Data Model**: Defines the structure and organization of time series data with tables and schemas - **Transport Layer**: Handles communication logistics, request routing, and client management - **Network Layer**: Manages low-level protocol communications using Arrow Flight and gRPC ## How To Use ### Installation 1. Install the Java Development Kit(JDK) Make sure that your system has JDK 8 or later installed. For more information on how to check your version of Java and install the JDK, see the [Oracle Overview of JDK Installation documentation](https://www.oracle.com/java/technologies/javase-downloads.html) 2. Add GreptimeDB Java SDK as a Dependency If you are using [Maven](https://maven.apache.org/), add the following to your pom.xml dependencies list: ```xml io.greptime ingester-all 0.15.0 ``` The latest version can be viewed [here](https://central.sonatype.com/search?q=io.greptime&name=ingester-all). After configuring your dependencies, make sure they are available to your project. This may require refreshing the project in your IDE or running the dependency manager. ### Client Initialization The entry point to the GreptimeDB Ingester Java client is the `GreptimeDB` class. You create a client instance by calling the static create method with appropriate configuration options. ```java // GreptimeDB has a default database named "public" in the default catalog "greptime", // we can use it as the test database String database = "public"; // By default, GreptimeDB listens on port 4001 using the gRPC protocol. // We can provide multiple endpoints that point to the same GreptimeDB cluster. // The client will make calls to these endpoints based on a load balancing strategy. // The client performs regular health checks and automatically routes requests to healthy nodes, // providing fault tolerance and improved reliability for your application. String[] endpoints = {"127.0.0.1:4001"}; // Sets authentication information. AuthInfo authInfo = new AuthInfo("username", "password"); GreptimeOptions opts = GreptimeOptions.newBuilder(endpoints, database) // If the database does not require authentication, we can use `AuthInfo.noAuthorization()` as the parameter. .authInfo(authInfo) // Enable secure connection if your server is secured by TLS //.tlsOptions(new TlsOptions()) // A good start ^_^ .build(); // Initialize the client // NOTE: The client instance is thread-safe and should be reused as a global singleton // for better performance and resource utilization. GreptimeDB client = GreptimeDB.create(opts); ``` ### Writing Data The ingester provides a unified approach for writing data to GreptimeDB through the `Table` abstraction. All data writing operations, including high-level APIs, are built on top of this fundamental structure. To write data, you create a `Table` with your time series data and write it to the database. #### Creating and Writing Tables Define a table schema and create a table: ```java // Create a table schema TableSchema schema = TableSchema.newBuilder("metrics") .addTag("host", DataType.String) .addTag("region", DataType.String) .addField("cpu_util", DataType.Float64) .addField("memory_util", DataType.Float64) .addTimestamp("ts", DataType.TimestampMillisecond) .build(); // Create a table from the schema Table table = Table.from(schema); // Add rows to the table // The values must be provided in the same order as defined in the schema // In this case: addRow(host, region, cpu_util, memory_util, ts) table.addRow("host1", "us-west-1", 0.42, 0.78, System.currentTimeMillis()); table.addRow("host2", "us-west-2", 0.46, 0.66, System.currentTimeMillis()); // Add more rows // .. // Complete the table to make it immutable. This finalizes the table for writing. // If users forget to call this method, it will automatically be called internally // before the table data is written. table.complete(); // Write the table to the database CompletableFuture> future = client.write(table); ``` GreptimeDB supports storing complex data structures using [JSON type data](/reference/sql/data-types.md#json-type-experimental). You can define JSON columns in your table schema and insert data using Map objects: ```java // Construct the table schema for sensor_readings TableSchema sensorReadings = TableSchema.newBuilder("sensor_readings") // The code for creating other columns is omitted // ... // specify the column type as JSON .addField("attributes", DataType.Json) .build(); // ... // Use map to insert JSON data Map attr = new HashMap<>(); attr.put("location", "factory-1"); Table table = Table.from(sensorReadings); table.addRow(... , attr); ``` ##### TableSchema The `TableSchema` defines the structure for writing data to GreptimeDB. It specifies the table structure including column names, semantic types, and data types. For detailed information about column semantic types (`Tag`, `Timestamp`, `Field`), refer to the [Data Model](/user-guide/concepts/data-model.md) documentation. ##### Table The `Table` interface represents data that can be written to GreptimeDB. It provides methods for adding rows and manipulating the data. Essentially, `Table` temporarily stores data in memory, allowing you to accumulate multiple rows for batch processing before sending them to the database, which significantly improves write efficiency compared to writing individual rows. A table goes through several distinct lifecycle stages: 1. **Creation**: Initialize a table from a schema using `Table.from(schema)` 2. **Data Addition**: Populate the table with rows using `addRow()` method 3. **Completion**: Finalize the table with `complete()` when all rows have been added 4. **Writing**: Send the completed table to the database Important considerations: - Tables are not thread-safe and should be accessed from a single thread - Tables cannot be reused after writing - create a new instance for each write operation - The associated `TableSchema` is immutable and can be safely reused across multiple operations ### Write Operations Although the time series table is created automatically when writing data to GreptimeDB via the SDK, you can still configure table options. The SDK supports the following table options: - `auto_create_table`: Default is `True`. If set to `False`, it indicates that the table already exists and does not need automatic creation, which can improve write performance. - `ttl`, `append_mode`, `merge_mode`: For more details, refer to the [table options](/reference/sql/create.md#table-options). You can set table options using the `Context`. For example, to set the `ttl` option, use the following code: ```java Context ctx = Context.newDefault(); // Add a hint to make the database create a table with the specified TTL (time-to-live) ctx = ctx.withHint("ttl", "3d"); // Set the compression algorithm to Zstd. ctx = ctx.withCompression(Compression.Zstd); // Use the ctx when writing data to GreptimeDB CompletableFuture> future = client.write(Arrays.asList(table1, table2), WriteOp.Insert, ctx); ``` For how to write data to GreptimeDB, see the following sections. ### Batching Write Batching write allows you to write data to multiple tables in a single request. It returns a `CompletableFuture>` and provides good performance through asynchronous execution. This is the recommended way to write data to GreptimeDB for most use cases. ```java // The batching write API CompletableFuture> future = client.write(table1, table2, table3); // For performance reasons, the SDK is designed to be purely asynchronous. // The return value is a CompletableFuture object. If you want to immediately obtain // the result, you can call `future.get()`, which will block until the operation completes. // For production environments, consider using non-blocking approaches with callbacks or // the CompletableFuture API. Result result = future.get(); ``` ### Streaming Write The streaming write API maintains a persistent connection to GreptimeDB for continuous data ingestion with rate limiting. It allows writing data from multiple tables through a single stream. Use this API when you need: - Continuous data collection with moderate volume - Writing to multiple tables via one connection - Cases where simplicity and convenience are more important than maximum throughput ```java // Create a stream writer StreamWriter writer = client.streamWriter(); // Write multiple tables writer.write(table1) .write(table2) .write(table3); // Complete the stream and get the result CompletableFuture result = writer.completed(); ``` You can also set a rate limit for stream writing: ```java // Limit to 1000 points per second StreamWriter writer = client.streamWriter(1000); ``` ### Bulk Write The Bulk Write API provides a high-performance, memory-efficient mechanism for ingesting large volumes of time-series data into GreptimeDB. It leverages off-heap memory management to achieve optimal throughput when writing batches of data. **Important**: 1. **Manual Table Creation Required**: Bulk API does **not** create tables automatically. You must create the table beforehand using either: - Insert API (which supports auto table creation), or - SQL DDL statements (CREATE TABLE) 2. **Schema Matching**: The table template in bulk API must exactly match the existing table schema. This API supports writing to one table per stream and handles large data volumes (up to 200MB per write) with adaptive flow control. Performance advantages include: - Off-heap memory management with Arrow buffers - Efficient binary serialization and data transfer - Optional compression - Batched operations This approach is particularly well-suited for: - Large-scale batch processing and data migrations - High-throughput log and sensor data ingestion - Time-series applications with demanding performance requirements - Systems processing high-frequency data collection Here's a typical pattern for using the Bulk Write API: ```java // Create a BulkStreamWriter with the table schema try (BulkStreamWriter writer = greptimeDB.bulkStreamWriter(schema)) { // Write multiple batches for (int batch = 0; batch < batchCount; batch++) { // Get a TableBufferRoot for this batch Table.TableBufferRoot table = writer.tableBufferRoot(1000); // column buffer size // Add rows to the batch for (int row = 0; row < rowsPerBatch; row++) { Object[] rowData = generateRow(batch, row); table.addRow(rowData); } // Complete the table to prepare for transmission table.complete(); // Send the batch and get a future for completion CompletableFuture future = writer.writeNext(); // Wait for the batch to be processed (optional) Integer affectedRows = future.get(); System.out.println("Batch " + batch + " wrote " + affectedRows + " rows"); } // Signal completion of the stream writer.completed(); } ``` #### Configuration The Bulk Write API can be configured with several options to optimize performance: ```java BulkWrite.Config cfg = BulkWrite.Config.newBuilder() .allocatorInitReservation(64 * 1024 * 1024L) // Customize memory allocation: 64MB initial reservation .allocatorMaxAllocation(4 * 1024 * 1024 * 1024L) // Customize memory allocation: 4GB max allocation .timeoutMsPerMessage(60 * 1000) // 60 seconds timeout per request .maxRequestsInFlight(8) // Concurrency Control: Configure with 8 maximum in-flight requests .build(); // Enable Zstd compression Context ctx = Context.newDefault().withCompression(Compression.Zstd); BulkStreamWriter writer = greptimeDB.bulkStreamWriter(schema, cfg, ctx); ``` ### Resource Management It's important to properly shut down the client when you're finished using it: ```java // Gracefully shut down the client client.shutdownGracefully(); ``` ### Performance Tuning #### Compression Options The ingester supports various compression algorithms to reduce network bandwidth and improve throughput. ```java // Set the compression algorithm to Zstd Context ctx = Context.newDefault().withCompression(Compression.Zstd); ``` #### Write Operation Comparison Understanding the performance characteristics of different write methods is crucial for optimizing data ingestion. | Write Method | API | Throughput | Latency | Memory Efficiency | CPU Usage | Best For | Limitations | |--------------|-----|------------|---------|-------------------|-----------|----------|-------------| | Batching Write | `write(tables)` | Better | Good | High | Higher | Simple applications, low latency requirements | Lower throughput for large volumes | | Streaming Write | `streamWriter()` | Moderate | Good | Moderate | Moderate | Continuous data streams, moderate throughput | More complex to use than regular writes | | Bulk Write | `bulkStreamWriter()` | Best | Higher | Best | Moderate | Maximum throughput, large batch operations | Higher latency, requires manual table creation | #### Buffer Size Optimization When using `BulkStreamWriter`, you can configure the column buffer size: ```java // Get the table buffer with a specific column buffer size Table.TableBufferRoot table = bulkStreamWriter.tableBufferRoot(columnBufferSize); ``` This option can significantly improve the speed of data conversion to the underlying format. For optimal performance, we recommend setting the column buffer size to 1024 or larger, depending on your specific workload characteristics and available memory. ### Export Metrics The ingester exposes comprehensive metrics that enable you to monitor its performance, health, and operational status. For detailed information about available metrics and their usage, refer to the [Ingester Prometheus Metrics](https://github.com/GreptimeTeam/greptimedb-ingester-java/tree/main/ingester-prometheus-metrics) documentation. ## Key Configuration Options `GreptimeOptions` is the main configuration class for the GreptimeDB Java client, used to configure client connections, write options, RPC settings, and various other parameters. For production environments, you may need to configure these commonly used options. Complete reference: [GreptimeOptions JavaDoc](https://javadoc.io/static/io.greptime/ingester-protocol/0.15.0/io/greptime/options/GreptimeOptions.html). **Key Options:** - `database`: Target database name, format `[catalog-]schema` (default: `public`) - `authInfo`: Authentication credentials for production environments - `rpcOptions.defaultRpcTimeout`: RPC request timeout (default: 60 seconds) - `writeMaxRetries`: Maximum retries for failed writes (default: 1) - `maxInFlightWritePoints`: Maximum data points in-flight for write flow control (default: 655360) - `writeLimitedPolicy`: Policy when write flow limit exceeded (default: AbortOnBlockingTimeoutPolicy 3s) - `defaultStreamMaxWritePointsPerSecond`: Rate limit for StreamWriter (default: 655360) ```java // Production-ready configuration RpcOptions rpcOpts = RpcOptions.newDefault(); rpcOpts.setDefaultRpcTimeout(30000); // 30 seconds timeout AuthInfo authInfo = new AuthInfo("username", "password"); GreptimeOptions options = GreptimeOptions.newBuilder("127.0.0.1:4001", "production_db") .authInfo(authInfo) .rpcOptions(rpcOpts) .writeMaxRetries(3) .maxInFlightWritePoints(1000000) .writeLimitedPolicy(new LimitedPolicy.AbortOnBlockingTimeoutPolicy(5, TimeUnit.SECONDS)) .defaultStreamMaxWritePointsPerSecond(50000) .build(); ``` ## FAQ ### Why am I getting some connection exceptions? When using the GreptimeDB Java ingester SDK, you may encounter some connection exceptions. For example, exceptions that are "`Caused by: java.nio.channels.UnsupportedAddressTypeException`", "`Caused by: java.net.ConnectException: connect(..) failed: Address family not supported by protocol`", or "`Caused by: java.net.ConnectException: connect(..) failed: Invalid argument`". While you are certain that the GreptimeDB server is running, and its endpoint is reachable. These connection exceptions could be all because the gRPC's `io.grpc.NameResolverProvider` service provider is not packaged into the final JAR, during the assembling process. So the fix can be: - If you are using Maven Assembly plugin, add the `metaInf-services` container descriptor handler to your assembly file, like this: ```xml ... metaInf-services ``` - And if you are using Maven Shade plugin, you can add the `ServicesResourceTransformer` instead: ```xml ... org.apache.maven.plugins maven-shade-plugin 3.6.0 shade ... ``` ## API Documentation and Examples - [API Reference](https://javadoc.io/doc/io.greptime/ingester-protocol/latest/index.html) - [Examples](https://github.com/GreptimeTeam/greptimedb-ingester-java/tree/main/ingester-example/) --- ## gRPC SDKs This guide will demonstrate how to use client libraries to write data in GreptimeDB. - [Go](go.md) - [Java](java.md) - [Rust](https://github.com/GreptimeTeam/greptimedb-ingester-rust) - [.NET](https://github.com/GreptimeTeam/greptimedb-ingester-dotnet) - [Erlang](https://github.com/GreptimeTeam/greptimedb-ingester-erl) - [TypeScript](https://github.com/GreptimeTeam/greptimedb-ingester-ts) --- ## InfluxDB Line Protocol GreptimeDB supports HTTP InfluxDB Line protocol. ## Ingest data ### Protocols #### Post metrics You can write data to GreptimeDB using the InfluxDB-compatible write APIs. Here's an example of how to use these APIs: ```shell curl -i -XPOST "http://localhost:4000/v1/influxdb/api/v2/write?bucket=public&precision=ms" \ -H "authorization: token " \ --data-binary \ 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4 1667446797450 monitor,host=127.0.0.2 cpu=0.2,memory=0.3 1667446798450 monitor,host=127.0.0.1 cpu=0.5,memory=0.2 1667446798450' ``` ```shell curl -i -XPOST "http://localhost:4000/v1/influxdb/write?db=public&precision=ms&u=&p=" \ --data-binary \ 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4 1667446797450 monitor,host=127.0.0.2 cpu=0.2,memory=0.3 1667446798450 monitor,host=127.0.0.1 cpu=0.5,memory=0.2 1667446798450' ``` The InfluxDB-compatible write APIs support the following query parameters: * `bucket`: Specifies the database to write to when using the InfluxDB line protocol V2 API. The default value is `public`. * `db`: Specifies the database to write to. This is the standard parameter for the InfluxDB line protocol V1 API. The V2 API also accepts `db` as a GreptimeDB compatibility alias, but `bucket` is recommended for V2. * `precision`: Defines the precision of the timestamp provided in the request body. Accepted values are `ns` (nanoseconds), `us` (microseconds), `ms` (milliseconds), and `s` (seconds). The data type of timestamps written by this API is `TimestampNanosecond`, so the default precision is `ns` (nanoseconds). If you use timestamps with other precisions in the request body, you need to specify the precision using this parameter. This parameter ensures that timestamp values are accurately interpreted and stored with nanosecond precision. You can pass HTTP hints to set options on auto-created tables. For example, set the `append_mode` hint to `true` to create an append-only table: ```shell curl -i -XPOST "http://localhost:4000/v1/influxdb/write?db=public&precision=ms&u=&p=" \ -H 'x-greptime-hint-append_mode: true' \ --data-binary \ 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4 1667446797450' ``` When `append_mode=true` is explicitly provided, GreptimeDB creates the table with `append_mode = 'true'` and `merge_mode = 'last_row'`. Otherwise, auto-created InfluxDB line protocol tables use the merge mode from the `merge_mode` HTTP hint. If no hint is provided, GreptimeDB uses the configured [`influxdb.default_merge_mode`](/user-guide/deployments-administration/configuration.md) value, which defaults to `last_non_null`. To change the cluster-wide default for InfluxDB line protocol auto-created tables, set this option in the standalone or frontend configuration file: ```toml [influxdb] default_merge_mode = "last_row" ``` Available values are `last_non_null` and `last_row`. You can also omit the timestamp when sending requests. GreptimeDB will use the current system time (in UTC) of the host machine as the timestamp. For example: ```shell curl -i -XPOST "http://localhost:4000/v1/influxdb/api/v2/write?bucket=public" \ -H "authorization: token " \ --data-binary \ 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4 monitor,host=127.0.0.2 cpu=0.2,memory=0.3 monitor,host=127.0.0.1 cpu=0.5,memory=0.2' ``` ```shell curl -i -XPOST "http://localhost:4000/v1/influxdb/write?db=public&u=&p=" \ --data-binary \ 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4 monitor,host=127.0.0.2 cpu=0.2,memory=0.3 monitor,host=127.0.0.1 cpu=0.5,memory=0.2' ``` #### Authentication GreptimeDB is compatible with InfluxDB's line protocol authentication format, both V1 and V2. If you have [configured authentication](/user-guide/deployments-administration/authentication/overview.md) in GreptimeDB, you need to provide the username and password in the HTTP request. For the InfluxDB line protocol V2 API, pass the username and password in the `Authorization` header using the `token :` format. ```shell curl 'http://localhost:4000/v1/influxdb/api/v2/write?bucket=public' \ -H 'authorization: token ' \ -d 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4' ``` For the InfluxDB line protocol V1 API, add `u` for user and `p` for password to the HTTP query string: ```shell curl 'http://localhost:4000/v1/influxdb/write?db=public&u=&p=' \ -d 'monitor,host=127.0.0.1 cpu=0.1,memory=0.4' ``` ### Telegraf GreptimeDB's support for the [InfluxDB line protocol](../for-iot/influxdb-line-protocol.md) ensures its compatibility with Telegraf. To configure Telegraf, simply add GreptimeDB URL into Telegraf configurations: ```toml [[outputs.influxdb_v2]] urls = ["http://:4000/v1/influxdb"] token = ":" bucket = "" ## Leave empty organization = "" ``` ```toml [[outputs.influxdb]] urls = ["http://:4000/v1/influxdb"] database = "" username = "" password = "" ``` ## Data model While you may already be familiar with [InfluxDB key concepts](https://docs.influxdata.com/influxdb/v2/reference/key-concepts/), the [data model](/user-guide/concepts/data-model.md) of GreptimeDB is something new to explore. Here are the similarities and differences between the data models of GreptimeDB and InfluxDB: - Both solutions are [schemaless](/user-guide/ingest-data/overview.md#automatic-schema-generation), eliminating the need to define a schema before writing data. - The GreptimeDB table is automatically created with the [`merge_mode` option](/reference/sql/create.md#create-a-table-with-merge-mode) set from the `merge_mode` HTTP hint or from [`influxdb.default_merge_mode`](/user-guide/deployments-administration/configuration.md) when the hint is absent. The configuration defaults to `last_non_null`. With `last_non_null`, the table merges rows with the same tags and timestamp by keeping the latest value of each field, which is the same behavior as InfluxDB. If the request explicitly sets the HTTP `append_mode` hint to `true`, the table is created with `append_mode = 'true'` and `merge_mode = 'last_row'`. - In InfluxDB, a point represents a single data record with a measurement, tag set, field set, and a timestamp. In GreptimeDB, it is represented as a row of data in the time-series table, where the table name aligns with the measurement, and the columns are divided into three types: Tag, Field, and Timestamp. - GreptimeDB uses `TimestampNanosecond` as the data type for timestamp data from the [InfluxDB line protocol API](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md). - GreptimeDB uses `Float64` as the data type for numeric data from the InfluxDB line protocol API. Consider the following [sample data](https://docs.influxdata.com/influxdb/v2/reference/key-concepts/data-elements/#sample-data) borrowed from InfluxDB docs as an example: |_time|_measurement|location|scientist|_field|_value| |---|---|---|---|---|---| |2019-08-18T00:00:00Z|census|klamath|anderson|bees|23| |2019-08-18T00:00:00Z|census|portland|mullen|ants|30| |2019-08-18T00:06:00Z|census|klamath|anderson|bees|28| |2019-08-18T00:06:00Z|census|portland|mullen|ants|32| The data mentioned above is formatted as follows in the InfluxDB line protocol: ```shell census,location=klamath,scientist=anderson bees=23 1566086400000000000 census,location=portland,scientist=mullen ants=30 1566086400000000000 census,location=klamath,scientist=anderson bees=28 1566086760000000000 census,location=portland,scientist=mullen ants=32 1566086760000000000 ``` In the GreptimeDB data model, the data is represented as follows in the `census` table: ```sql +---------------------+----------+-----------+------+------+ | greptime_timestamp | location | scientist | bees | ants | +---------------------+----------+-----------+------+------+ | 2019-08-18 00:00:00 | klamath | anderson | 23 | NULL | | 2019-08-18 00:06:00 | klamath | anderson | 28 | NULL | | 2019-08-18 00:00:00 | portland | mullen | NULL | 30 | | 2019-08-18 00:06:00 | portland | mullen | NULL | 32 | +---------------------+----------+-----------+------+------+ ``` The schema of the `census` table is as follows: ```sql +--------------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+----------------------+------+------+---------+---------------+ | location | String | PRI | YES | | TAG | | scientist | String | PRI | YES | | TAG | | bees | Float64 | | YES | | FIELD | | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | ants | Float64 | | YES | | FIELD | +--------------------+----------------------+------+------+---------+---------------+ ``` ## Reference - [InfluxDB Line protocol](https://docs.influxdata.com/influxdb/v2.7/reference/syntax/line-protocol/) --- ## Kafka(For-iot) Please refer to the [Kafka documentation](/user-guide/ingest-data/for-observability/kafka.md) for instructions on how to ingest data from Kafka into GreptimeDB. --- ## OpenTSDB GreptimeDB supports ingesting OpenTSDB via HTTP API. GreptimeDB also supports inserting OpenTSDB metrics via HTTP endpoints. We use the request and response format described in OpenTSDB's `/api/put`. The HTTP endpoint in GreptimeDB for handling metrics is `/v1/opentsdb/api/put`. Starting GreptimeDB, the HTTP server is listening on port `4000` by default. Use curl to insert one metric point: ```shell curl -X POST http://127.0.0.1:4000/v1/opentsdb/api/put \ -H 'Authorization: Basic ' \ -d ' { "metric": "sys.cpu.nice", "timestamp": 1667898896, "value": 18, "tags": { "host": "web01", "dc": "hz" } } ' ``` Or insert multiple metric points: ```shell curl -X POST http://127.0.0.1:4000/v1/opentsdb/api/put \ -H 'Authorization: Basic ' \ -d ' [ { "metric": "sys.cpu.nice", "timestamp": 1667898896, "value": 1, "tags": { "host": "web02", "dc": "hz" } }, { "metric": "sys.cpu.nice", "timestamp": 1667898897, "value": 9, "tags": { "host": "web03", "dc": "sh" } } ] ' ``` --- ## Ingest Data for IoT IoT data reaches GreptimeDB from devices, brokers, and applications. [Ingest Data](../overview.md) lists every source in one table; this page covers the IoT paths. - [gRPC SDKs](./grpc-sdks/overview.md) — clients for Go, Java, Rust, .NET, Erlang, and TypeScript. - [EMQX](emqx.md) — MQTT devices, written through an EMQX data integration. - [InfluxDB Line Protocol](influxdb-line-protocol.md) — the path for migrating off InfluxDB, and for Telegraf. This page also covers the Telegraf configuration. - [OpenTSDB](opentsdb.md) — the `/v1/opentsdb/api/put` HTTP endpoint. - [SQL](sql.md) — `INSERT` statements over the MySQL or PostgreSQL protocol. Tables and columns are created as data arrives, except on the SQL path. See [Automatic Schema Generation](../overview.md#automatic-schema-generation). --- ## SQL You can execute SQL statements using [MySQL](/user-guide/protocols/mysql.md) or [PostgreSQL](/user-guide/protocols/postgresql.md) clients, and access GreptimeDB using the MySQL or PostgreSQL protocol with any programming language of your preference, such as Java JDBC. We will use the `monitor` table as an example to show how to write data. ## Create a table Before inserting data, you need to create a table. For example, create a table named `monitor`: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64 DEFAULT 0, memory FLOAT64, PRIMARY KEY(host)); ``` The above statement will create a table with the following schema: ```sql +--------+----------------------+------+------+---------------------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------+----------------------+------+------+---------------------+---------------+ | host | String | PRI | YES | | TAG | | ts | TimestampMillisecond | PRI | NO | current_timestamp() | TIMESTAMP | | cpu | Float64 | | YES | 0 | FIELD | | memory | Float64 | | YES | | FIELD | +--------+----------------------+------+------+---------------------+---------------+ 4 rows in set (0.01 sec) ``` For more information about the `CREATE TABLE` statement, please refer to [table management](/user-guide/deployments-administration/manage-data/basic-table-operations.md#create-a-table). ## Insert data Let's insert some testing data to the `monitor` table. You can use the `INSERT INTO` SQL statements: ```sql INSERT INTO monitor VALUES ("127.0.0.1", 1702433141000, 0.5, 0.2), ("127.0.0.2", 1702433141000, 0.3, 0.1), ("127.0.0.1", 1702433146000, 0.3, 0.2), ("127.0.0.2", 1702433146000, 0.2, 0.4), ("127.0.0.1", 1702433151000, 0.4, 0.3), ("127.0.0.2", 1702433151000, 0.2, 0.4); ``` ```sql Query OK, 6 rows affected (0.01 sec) ``` You can also insert data by specifying the column names: ```sql INSERT INTO monitor (host, ts, cpu, memory) VALUES ("127.0.0.1", 1702433141000, 0.5, 0.2), ("127.0.0.2", 1702433141000, 0.3, 0.1), ("127.0.0.1", 1702433146000, 0.3, 0.2), ("127.0.0.2", 1702433146000, 0.2, 0.4), ("127.0.0.1", 1702433151000, 0.4, 0.3), ("127.0.0.2", 1702433151000, 0.2, 0.4); ``` Through the above statement, we have inserted six rows into the `monitor` table. For more information about the `INSERT` statement, please refer to [`INSERT`](/reference/sql/insert.md). ## Time zone The time zone specified in the SQL client will affect the timestamp with a string format that does not have time zone information. The timestamp value will automatically have the client's time zone information added. For example, the following SQL set the time zone to `+8:00`: ```sql SET time_zone = '+8:00'; ``` Then insert values into the `monitor` table: ```sql INSERT INTO monitor (host, ts, cpu, memory) VALUES ("127.0.0.1", "2024-01-01 00:00:00", 0.4, 0.1), ("127.0.0.2", "2024-01-01 00:00:00+08:00", 0.5, 0.1); ``` The first timestamp value `2024-01-01 00:00:00` does not have time zone information, so it will automatically have the client's time zone information added. After inserting, it will be equivalent to the second value `2024-01-01 00:00:00+08:00`. The result in the `+8:00` time zone client is as follows: ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-01-01 00:00:00 | 0.4 | 0.1 | | 127.0.0.2 | 2024-01-01 00:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ ``` The result in the `UTC` time zone client is as follows: ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2023-12-31 16:00:00 | 0.4 | 0.1 | | 127.0.0.2 | 2023-12-31 16:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ ``` --- ## Grafana Alloy [Grafana Alloy](https://grafana.com/docs/alloy/latest/) is an observability data pipeline for OpenTelemetry (OTel), Prometheus, Pyroscope, Loki, and many other metrics, logs, traces, and profiling tools. You can integrate GreptimeDB as a data sink for Alloy. ## Prometheus Remote Write Configure GreptimeDB as remote write target: ```hcl prometheus.remote_write "greptimedb" { endpoint { url = coalesce(sys.env("GREPTIME_SCHEME"), "http") + "://" + coalesce(sys.env("GREPTIME_HOST"), "greptimedb") + ":" + coalesce(sys.env("GREPTIME_PORT"), "4000") + "/v1/prometheus/write?db=" + coalesce(sys.env("GREPTIME_DB"), "public") basic_auth { username = sys.env("GREPTIME_USERNAME") password = sys.env("GREPTIME_PASSWORD") } } } ``` - `GREPTIME_HOST`: GreptimeDB host address, e.g., `localhost`. - `GREPTIME_DB`: GreptimeDB database name, default is `public`. - `GREPTIME_USERNAME` and `GREPTIME_PASSWORD`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. For details on the data model transformation from Prometheus to GreptimeDB, refer to the [Data Model](/user-guide/ingest-data/for-observability/prometheus.md#data-model) section in the Prometheus Remote Write guide. ## OpenTelemetry GreptimeDB can also be configured as OpenTelemetry collector. ### Metrics ```hcl otelcol.exporter.otlphttp "greptimedb" { client { endpoint = coalesce(sys.env("GREPTIME_SCHEME"), "http") + "://" + coalesce(sys.env("GREPTIME_HOST"), "greptimedb") + ":" + coalesce(sys.env("GREPTIME_PORT"), "4000") + "/v1/otlp/" headers = { "X-Greptime-DB-Name" = coalesce(sys.env("GREPTIME_DB"), "public"), } auth = otelcol.auth.basic.credentials.handler } } otelcol.auth.basic "credentials" { username = sys.env("GREPTIME_USERNAME") password = sys.env("GREPTIME_PASSWORD") } ``` - `GREPTIME_HOST`: GreptimeDB host address, e.g., `localhost`. - `GREPTIME_DB`: GreptimeDB database name, default is `public`. - `GREPTIME_USERNAME` and `GREPTIME_PASSWORD`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. For details on the metrics data model transformation from OpenTelemetry to GreptimeDB, refer to the [Data Model](/user-guide/ingest-data/for-observability/opentelemetry.md#data-model) section in the OpenTelemetry guide. ### Logs This example sends logs to GreptimeDB through an OpenTelemetry pipeline. For production log pipelines, add an explicit batch processor before the exporter. See the [Batching](#batching) section for details. ```hcl loki.source.file "greptime" { targets = [ {__path__ = "/tmp/foo.txt"}, ] forward_to = [otelcol.receiver.loki.greptime.receiver] } otelcol.receiver.loki "greptime" { output { logs = [otelcol.processor.batch.greptimedb_logs.input] } } otelcol.processor.batch "greptimedb_logs" { send_batch_size = 5000 send_batch_max_size = 10000 timeout = "1s" output { logs = [otelcol.exporter.otlphttp.greptimedb_logs.input] } } otelcol.auth.basic "credentials" { username = sys.env("GREPTIME_USERNAME") password = sys.env("GREPTIME_PASSWORD") } otelcol.exporter.otlphttp "greptimedb_logs" { client { endpoint = coalesce(sys.env("GREPTIME_SCHEME"), "http") + "://" + coalesce(sys.env("GREPTIME_HOST"), "greptimedb") + ":" + coalesce(sys.env("GREPTIME_PORT"), "4000") + "/v1/otlp/" headers = { "X-Greptime-DB-Name" = coalesce(sys.env("GREPTIME_DB"), "public"), "X-Greptime-Log-Table-Name" = coalesce(sys.env("GREPTIME_LOG_TABLE_NAME"), "demo_logs"), "X-Greptime-Log-Extract-Keys" = "filename,log.file.name,loki.attribute.labels", } auth = otelcol.auth.basic.credentials.handler } sending_queue { queue_size = 10000 num_consumers = 10 } } ``` - `GREPTIME_HOST`: GreptimeDB host address, such as `localhost`. - `GREPTIME_DB`: GreptimeDB database name, default is `public`. - `GREPTIME_LOG_TABLE_NAME`: Target log table name, default is `demo_logs`. - `GREPTIME_USERNAME` and `GREPTIME_PASSWORD`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. - `X-Greptime-Log-Extract-Keys`: Keys extracted from OTLP log attributes. See the [OTLP/HTTP API documentation](/user-guide/ingest-data/for-observability/opentelemetry.md#otlphttp-api-1) for details. For details on the log data model transformation from OpenTelemetry to GreptimeDB, refer to the [Data Model](/user-guide/ingest-data/for-observability/opentelemetry.md#data-model-1) section in the OpenTelemetry guide. ## Loki GreptimeDB also supports the Loki push protocol for logs. If your Alloy pipeline is already built with Loki components, we recommend using the native Loki ingestion path first. See the [Loki guide](/user-guide/ingest-data/for-observability/loki.md) for the Loki protocol details and data model mapping. ### Logs This example uses only Loki components to read, process, and send logs to GreptimeDB through the Loki push API: ```hcl loki.source.file "greptime" { targets = [ {__path__ = "/tmp/foo.txt"}, ] forward_to = [loki.process.greptime.receiver] } loki.process "greptime" { forward_to = [loki.write.greptimedb.receiver] stage.static_labels { values = { job = "greptime", from = "alloy", } } } loki.write "greptimedb" { endpoint { url = coalesce(sys.env("GREPTIME_SCHEME"), "http") + "://" + coalesce(sys.env("GREPTIME_HOST"), "greptimedb") + ":" + coalesce(sys.env("GREPTIME_PORT"), "4000") + "/v1/loki/api/v1/push" headers = { "X-Greptime-DB-Name" = coalesce(sys.env("GREPTIME_DB"), "public"), "X-Greptime-Log-Table-Name" = coalesce(sys.env("GREPTIME_LOG_TABLE_NAME"), "loki_demo_logs"), } basic_auth { username = sys.env("GREPTIME_USERNAME") password = sys.env("GREPTIME_PASSWORD") } } } ``` - `GREPTIME_HOST`: GreptimeDB host address, such as `localhost`. - `GREPTIME_DB`: GreptimeDB database name, default is `public`. - `GREPTIME_LOG_TABLE_NAME`: Target log table name, default is `loki_demo_logs`. - `GREPTIME_USERNAME` and `GREPTIME_PASSWORD`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. This configuration tails `/tmp/foo.txt`, adds two static labels, and sends the logs directly to GreptimeDB with `loki.write`. ## Batching `otelcol.exporter.otlphttp` does not enable batching by default. When Alloy reads a burst of logs, such as a large backlog from files or Docker containers, the exporter queue can fill before enough records are grouped together, which leads to errors like `sending queue is full`. Based on the behavior reported in production testing, enabling the exporter's internal `sending_queue.batch` block alone may still be insufficient for bursty log workloads. Putting `otelcol.processor.batch` in front of the exporter is a more reliable pattern for logs because the exporter receives larger bundled batches instead of many individual log records. If you use OTLP/HTTP for logs, we recommend this order: 1. `loki.source.*` 2. `otelcol.receiver.loki` 3. `otelcol.processor.batch` 4. `otelcol.exporter.otlphttp` If your pipeline is already Loki-native and you do not need OpenTelemetry processing, prefer `loki.write` and the Loki push protocol instead. :::tip NOTE Refer to the official Grafana Alloy documentation for the latest component behavior and tuning guidance, especially for `loki.write`, `otelcol.processor.batch`, and `otelcol.exporter.otlphttp`. ::: --- ## Elasticsearch ## Overview GreptimeDB supports data ingestion through Elasticsearch's [`_bulk` API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html). We map Elasticsearch's Index concept to GreptimeDB's Table, and users can specify the database name using the `db` URL parameter. **Unlike native Elasticsearch, this API only supports data insertion, not modification or deletion**. At the implementation level, both `index` and `create` commands in native Elasticsearch `_bulk` API requests are treated as **creation operations** by GreptimeDB. Additionally, GreptimeDB only parses the `_index` field from native `_bulk` API command requests while ignoring other fields. ## HTTP API In most log collectors(such as Logstash and Filebeat mentioned below), you only need to configure the HTTP endpoint like `http://${db_host}:${db_http_port}/v1/elasticsearch`, for example `http://localhost:4000/v1/elasticsearch`. GreptimeDB supports data ingestion through Elasticsearch protocol by implementing the following two HTTP endpoints: - **`/v1/elasticsearch/_bulk`**: Users can use the POST method to write data in NDJSON format to GreptimeDB. For example, the following request will create a table named `test` and insert two records: ```json POST /v1/elasticsearch/_bulk {"create": {"_index": "test", "_id": "1"}} {"name": "John", "age": 30} {"create": {"_index": "test", "_id": "2"}} {"name": "Jane", "age": 25} ``` - **`/v1/elasticsearch/${index}/_bulk`**: Users can use the POST method to write data in NDJSON format to the `${index}` table in GreptimeDB. If the POST request also contains an `_index` field, the `${index}` in the URL will be ignored. For example, the following request will create tables named `test` and `another_index`, and insert corresponding data: ```json POST /v1/elasticsearch/test/_bulk {"create": {"_id": "1"}} {"name": "John", "age": 30} {"create": {"_index": "another_index", "_id": "2"}} {"name": "Jane", "age": 25} ``` ### HTTP Headers - `x-greptime-db-name`: Specifies the database name. Defaults to `public` if not specified. - `x-greptime-pipeline-name`: Specifies the pipeline name. Defaults to GreptimeDB's internal pipeline `greptime_identity` if not specified. For more details about Pipeline, please refer to the [Manage Pipelines](/user-guide/logs/manage-pipelines.md) documentation. ### URL Parameters You can use the following HTTP URL parameters: - `db`: Specifies the database name. Defaults to `public` if not specified. - `pipeline_name`: Specifies the pipeline name. Defaults to GreptimeDB's internal pipeline `greptime_identity` if not specified. - `msg_field`: Specifies the JSON field name containing the original log data. For example, in Logstash and Filebeat, this field is typically `message`. If specified, GreptimeDB will attempt to parse the data in this field as JSON format. If parsing fails, the field will be treated as a string. This configuration option currently only takes effect in URL parameters. ### Authentication Header For detailed information about the authentication header, please refer to the [Authorization](/user-guide/protocols/http.md#authentication) documentation. ## Usage ### Use HTTP API to ingest data You can create a `request.json` file with the following content: ```json {"create": {"_index": "es_test", "_id": "1"}} {"name": "John", "age": 30} {"create": {"_index": "es_test", "_id": "2"}} {"name": "Jane", "age": 25} ``` Then use the `curl` command to send this file as a request body to GreptimeDB: ```bash curl -XPOST http://localhost:4000/v1/elasticsearch/_bulk \ -H "Authorization: Basic " \ -H "Content-Type: application/x-ndjson" \ --data-binary @request.json ``` We can use a `mysql` client to connect to GreptimeDB and execute the following SQL to view the inserted data: ```sql SELECT * FROM es_test; ``` We will see the following results: ``` mysql> SELECT * FROM es_test; +------+------+----------------------------+ | age | name | greptime_timestamp | +------+------+----------------------------+ | 30 | John | 2025-01-15 08:26:06.516665 | | 25 | Jane | 2025-01-15 08:26:06.521510 | +------+------+----------------------------+ 2 rows in set (0.13 sec) ``` ### Logstash If you are using [Logstash](https://www.elastic.co/logstash) to collect logs, you can use the following configuration to write data to GreptimeDB: ``` output { elasticsearch { hosts => ["http://localhost:4000/v1/elasticsearch"] index => "my_index" manage_template => false ilm_enabled => false data_stream => false parameters => { "msg_field" => "message" } } } ``` Please pay attention to the following GreptimeDB-related configurations: - `hosts`: Specifies the HTTP address of GreptimeDB's Elasticsearch protocol, which is `http://${db_host}:${db_http_port}/v1/elasticsearch`. - `index`: Specifies the table name that will be written to. - `manage_template`, `ilm_enabled`, and `data_stream`: Disable Elasticsearch index management features. GreptimeDB's Elasticsearch-compatible API supports log ingestion through `_bulk`, but does not manage Elasticsearch templates, ILM policies, or data streams. - `parameters`: Specifies the URL parameters for writing. The example above uses `msg_field=message`, which tells GreptimeDB to parse the original log content from the `message` field. You can also set `pipeline_name` here if you want to use an existing GreptimeDB pipeline. If `pipeline_name` is omitted, GreptimeDB uses the built-in `greptime_identity` pipeline. - If Logstash prints warnings about X-Pack monitoring trying to connect to `elasticsearch:9200`, disable Logstash monitoring by setting `xpack.monitoring.enabled: false` in `logstash.yml`. ### Filebeat If you are using [Filebeat](https://github.com/elastic/beats/tree/main/filebeat) to collect logs, you can use the following configuration to write data to GreptimeDB: ``` output.elasticsearch: hosts: ["http://localhost:4000/v1/elasticsearch"] index: "my_index" parameters: msg_field: message setup.ilm.enabled: false setup.template.enabled: false ``` Please pay attention to the following GreptimeDB-related configurations: - `hosts`: Specifies the HTTP address of GreptimeDB's Elasticsearch protocol, which is `http://${db_host}:${db_http_port}/v1/elasticsearch`. - `index`: Specifies the table name that will be written to. - `parameters`: Specifies the URL parameters for writing. The example above uses `msg_field: message`, which tells GreptimeDB to parse the original log content from the `message` field. You can also set `pipeline_name` here if you want to use an existing GreptimeDB pipeline. If `pipeline_name` is omitted, GreptimeDB uses the built-in `greptime_identity` pipeline. - `setup.ilm.enabled` and `setup.template.enabled`: Disable Elasticsearch index lifecycle management and template setup. GreptimeDB's Elasticsearch-compatible API supports log ingestion through `_bulk`, but does not manage Elasticsearch templates or ILM policies. ### Telegraf If you are using [Telegraf](https://github.com/influxdata/telegraf) to collect logs, you can use its Elasticsearch plugin to write data to GreptimeDB, as shown below: ```toml [[outputs.elasticsearch]] urls = [ "http://localhost:4000/v1/elasticsearch" ] index_name = "test_table" health_check_interval = "0s" enable_sniffer = false flush_interval = "1s" manage_template = false template_name = "telegraf" overwrite_template = false namepass = ["tail"] [outputs.elasticsearch.headers] "X-GREPTIME-DB-NAME" = "public" "X-GREPTIME-PIPELINE-NAME" = "greptime_identity" [[inputs.tail]] files = ["/tmp/test.log"] from_beginning = true data_format = "value" data_type = "string" character_encoding = "utf-8" interval = "1s" pipe = false watch_method = "inotify" ``` Please pay attention to the following GreptimeDB-related configurations: - `urls`: Specifies the HTTP address of GreptimeDB's Elasticsearch protocol, which is `http://${db_host}:${db_http_port}/v1/elasticsearch`. - `index_name`: Specifies the table name that will be written to. - `outputs.elasticsearch.header`: Specifies the HTTP Header for writing, where the example above specifies two parameters: `X-GREPTIME-DB-NAME` and `X-GREPTIME-PIPELINE-NAME`. --- ## Fluent Bit [Fluent Bit](http://fluentbit.io/) is a fast and lightweight telemetry agent for logs, metrics, and traces for Linux, macOS, Windows, and BSD family operating systems. Fluent Bit has been made with a strong focus on performance to allow the collection and processing of telemetry data from different sources without complexity. You can forward Fluent Bit data to GreptimeDB. This document describes how to configure Fluent Bit to send logs, metrics, and traces to GreptimeDB. ## HTTP Using Fluent Bit's [HTTP Output Plugin](https://docs.fluentbit.io/manual/pipeline/outputs/http), you can send logs to GreptimeDB. Before configuring Fluent Bit, ensure that you understand the [log ingestion flow](/user-guide/logs/overview.md) and [how to use pipelines](/user-guide/logs/use-custom-pipelines.md). ```conf [OUTPUT] Name http Match * Host greptimedb Port 4000 Uri /v1/ingest?db=public&table=your_table&pipeline_name=greptime_identity Format json Json_date_key scrape_timestamp Json_date_format iso8601 compress gzip http_User http_Passwd ``` - `host`: GreptimeDB host address, e.g., `localhost`. - `port`: GreptimeDB port, default is `4000`. - `uri`: The endpoint to send logs to. - `format`: The format of the logs, needs to be `json`. - `json_date_key`: The key in the JSON object that contains the timestamp. - `json_date_format`: The format of the timestamp. - `compress`: The compression method to use, e.g., `gzip`. - `header`: The header to send with the request, e.g., `Authorization` for authentication. - `http_user` and `http_passwd`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. In params Uri, - `db` is the database name you want to write logs to. - `table` is the table name you want to write logs to. - `pipeline_name` is the pipeline name you want to use for processing logs. - `custom_time_index` is optional. Use it when a field in the input data should become the GreptimeDB time index. The supported formats are `;epoch;` and `;datestr;`. The `greptime_identity` pipeline creates the table directly from the JSON fields. If you want a field such as `scrape_timestamp` to become the GreptimeDB time index instead of a regular column, set `custom_time_index` in the request URI or use a custom pipeline to parse and map that field. ## OpenTelemetry GreptimeDB can also be configured as OpenTelemetry collector. Using Fluent Bit's [OpenTelemetry Output Plugin](https://docs.fluentbit.io/manual/pipeline/outputs/opentelemetry), you can send metrics, logs, and traces to GreptimeDB. Use a separate output for each signal because GreptimeDB uses signal-specific headers. Assign a distinct tag to each signal in the Fluent Bit inputs, then match that tag in the corresponding output: ``` # Only for metrics [OUTPUT] Name opentelemetry Alias opentelemetry_metrics Match Host 127.0.0.1 Port 4000 Metrics_uri /v1/otlp/v1/metrics Log_response_payload True Tls Off Tls.verify Off Header X-Greptime-DB-Name "" # Only for logs [OUTPUT] Name opentelemetry Alias opentelemetry_logs Match Host 127.0.0.1 Port 4000 Logs_uri /v1/otlp/v1/logs Log_response_payload True Tls Off Tls.verify Off Header X-Greptime-Log-Table-Name "" Header X-Greptime-Pipeline-Name "" Header X-Greptime-DB-Name "" # Only for traces [OUTPUT] Name opentelemetry Alias opentelemetry_traces Match Host 127.0.0.1 Port 4000 Traces_uri /v1/otlp/v1/traces Log_response_payload True Tls Off Tls.verify Off Header X-Greptime-Pipeline-Name "greptime_trace_v1" Header X-Greptime-DB-Name "" ``` - `Metrics_uri`, `Logs_uri`, and `Traces_uri`: The endpoints for metrics, logs, and traces. - `Match`: The signal-specific tag configured on the corresponding Fluent Bit input. - `http_user` and `http_passwd`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. Add them to each output when authentication is enabled. In this example, the OpenTelemetry OTLP/HTTP API is used. For signal-specific headers and options, see the OpenTelemetry API sections for [metrics](/user-guide/ingest-data/for-observability/opentelemetry.md#otlphttp-api), [logs](/user-guide/ingest-data/for-observability/opentelemetry.md#otlphttp-api-1), and [traces](/user-guide/ingest-data/for-observability/opentelemetry.md#otlphttp-api-2). ## Prometheus Remote Write Configure GreptimeDB as remote write target: ``` [OUTPUT] Name prometheus_remote_write Match internal_metrics Host 127.0.0.1 Port 4000 Uri /v1/prometheus/write?db= Tls Off http_user http_passwd ``` - `Uri`: The endpoint to send metrics to. - `http_user` and `http_passwd`: The [authentication credentials](/user-guide/deployments-administration/authentication/static.md) for GreptimeDB. In params Uri, - `db` is the database name you want to write metrics to. For details on the data model transformation from Prometheus to GreptimeDB, refer to the [Data Model](/user-guide/ingest-data/for-observability/prometheus.md#data-model) section in the Prometheus Remote Write guide. --- ## InfluxDB Line Protocol(For-observability) Please refer to the [InfluxDB Line Protocol documentation](../for-iot/influxdb-line-protocol.md) for instructions on how to write data to GreptimeDB using the InfluxDB Line Protocol. --- ## Kafka(For-observability) If you are using Kafka or Kafka-compatible message queue for observability data transporting, it's possible to ingest data into GreptimeDB directly. Here we are using Vector as the tool to transport data from Kafka to GreptimeDB. ## Metrics When ingesting metrics from Kafka into GreptimeDB, messages should be formatted in InfluxDB line protocol. For example: ```txt census,location=klamath,scientist=anderson bees=23 1566086400000000000 ``` Then configure Vector to use the `influxdb` decoding codec to process these messages. ```toml [sources.metrics_mq] # Specifies that the source type is Kafka type = "kafka" # The consumer group ID for Kafka group_id = "vector0" # The list of Kafka topics to consume messages from topics = ["test_metric_topic"] # The address of the Kafka broker to connect to bootstrap_servers = "kafka:9092" # The `influxdb` means the messages are expected to be in InfluxDB line protocol format. decoding.codec = "influxdb" [sinks.metrics_in] inputs = ["metrics_mq"] # Specifies that the sink type is `greptimedb_metrics` type = "greptimedb_metrics" # The endpoint of the GreptimeDB server. # Replace with the actual hostname or IP address. endpoint = ":4001" dbname = "" username = "" password = "" tls = {} ``` For details on how InfluxDB line protocol metrics are mapped to GreptimeDB data, please refer to the [Data Model](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md#data-model) section in the InfluxDB line protocol documentation. When Vector decodes Kafka messages with `decoding.codec = "influxdb"` and writes them through the `greptimedb_metrics` sink, each InfluxDB field is converted into a metric name. For example, `census bees=23` is written to a table named `census_bees`, with the value stored in the `val` column. ## Logs Developers commonly work with two types of logs: JSON logs and plain text logs. Consider the following examples sent from Kafka. A plain text log: ```txt 127.0.0.1 - - [25/May/2024:20:16:37 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" ``` Or a JSON log: ```json { "timestamp": "2024-12-23T10:00:00Z", "level": "INFO", "message": "Service started" } ``` GreptimeDB transforms these logs into structured data with multiple columns and automatically creates the necessary tables. A pipeline processes the logs into structured data before ingestion into GreptimeDB. Different log formats require different [Pipelines](/user-guide/logs/use-custom-pipelines.md) for parsing. See the following sections for details. ### Logs with JSON format For logs in JSON format (e.g., `{"timestamp": "2024-12-23T10:00:00Z", "level": "INFO", "message": "Service started"}`), you can use the built-in [`greptime_identity`](/reference/pipeline/built-in-pipelines.md#greptime_identity) pipeline for direct ingestion. This pipeline creates columns automatically based on the fields in your JSON log message. Simply configure Vector's `transforms` settings to parse the JSON message and use the `greptime_identity` pipeline as shown in the following example: ```toml [sources.logs_in] type = "kafka" # The consumer group ID for Kafka group_id = "vector0" # The list of Kafka topics to consume messages from topics = ["test_log_topic"] # The address of the Kafka broker to connect to bootstrap_servers = "kafka:9092" # transform the log to JSON format [transforms.logs_json] type = "remap" inputs = ["logs_in"] source = ''' . = parse_json!(.message) ''' [sinks.logs_out] # Specifies that this sink will receive data from the `logs_json` source inputs = ["logs_json"] # Specifies that the sink type is `greptimedb_logs` type = "greptimedb_logs" # The endpoint of the GreptimeDB server endpoint = "http://:4000" compression = "gzip" # Replace , , and with the actual values dbname = "" username = "" password = "" # The table name in GreptimeDB, if it doesn't exist, it will be created automatically table = "demo_logs" # Use the built-in `greptime_identity` pipeline pipeline_name = "greptime_identity" ``` ### Logs with text format For logs in text format, such as the access log format below, you'll need to create a custom pipeline to parse them: ``` 127.0.0.1 - - [25/May/2024:20:16:37 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" ``` #### Create a pipeline To create a custom pipeline, please refer to the [using custom pipelines](/user-guide/logs/use-custom-pipelines.md) documentation for detailed instructions. #### Ingest data After creating the pipeline, configure it to the `pipeline_name` field in the Vector configuration file. ```toml # sample.toml [sources.log_mq] # Specifies that the source type is Kafka type = "kafka" # The consumer group ID for Kafka group_id = "vector0" # The list of Kafka topics to consume messages from topics = ["test_log_topic"] # The address of the Kafka broker to connect to bootstrap_servers = "kafka:9092" [sinks.sink_greptime_logs] # Specifies that the sink type is `greptimedb_logs` type = "greptimedb_logs" # Specifies that this sink will receive data from the `log_mq` source inputs = [ "log_mq" ] # Use `gzip` compression to save bandwidth compression = "gzip" # The endpoint of the GreptimeDB server # Replace with the actual hostname or IP address endpoint = "http://:4000" dbname = "" username = "" password = "" # The table name in GreptimeDB, if it doesn't exist, it will be created automatically table = "demo_logs" # The custom pipeline name that you created pipeline_name = "your_custom_pipeline" ``` ## Demo For a runnable demo of data transformation and ingestion, please refer to the [Kafka Ingestion Demo](https://github.com/GreptimeTeam/demo-scene/tree/main/kafka-ingestion). --- ## Loki ## Usage ### API To send logs to GreptimeDB through the raw HTTP API, use the following information: * **URL**: `http{s}:///v1/loki/api/v1/push` * **Headers**: * `X-Greptime-DB-Name`: `` * `Authorization`: `Basic` authentication, which is a Base64 encoded string of `:`. For more information, please refer to [Authentication](https://docs.greptime.com/user-guide/deployments-administration/authentication/static/) and [HTTP API](https://docs.greptime.com/user-guide/protocols/http#authentication). * `X-Greptime-Log-Table-Name`: `` (optional) - The table name to store the logs. If not provided, the default table name is `loki_logs`. GreptimeDB accepts either of the Loki Push API payload encodings: * **Protobuf**: Send a Snappy-compressed `PushRequest` matching [logproto.proto](https://github.com/grafana/loki/blob/main/pkg/logproto/logproto.proto) with `Content-Type: application/x-protobuf`. * **JSON**: Send a Loki JSON push body with `Content-Type: application/json`. ### Example Code [Grafana Alloy](https://grafana.com/docs/alloy/latest/) is a vendor-neutral distribution of the OpenTelemetry (OTel) Collector. Alloy uniquely combines the very best OSS observability signals in the community. It supplies a Loki exporter that can be used to send logs to GreptimeDB. Here is an example configuration: ``` loki.source.file "greptime" { targets = [ {__path__ = "/tmp/foo.txt"}, ] forward_to = [loki.write.greptime_loki.receiver] } loki.write "greptime_loki" { endpoint { url = coalesce(sys.env("GREPTIME_SCHEME"), "http") + "://" + coalesce(sys.env("GREPTIME_HOST"), "greptimedb") + ":" + coalesce(sys.env("GREPTIME_PORT"), "4000") + "/v1/loki/api/v1/push" headers = { "x-greptime-db-name" = coalesce(sys.env("GREPTIME_DB"), "public"), "x-greptime-log-table-name" = coalesce(sys.env("GREPTIME_LOG_TABLE_NAME"), "loki_demo_logs"), } } external_labels = { "job" = "greptime", "from" = "alloy", } } ``` This configuration reads logs from the file `/tmp/foo.txt` and sends them to GreptimeDB. The logs are stored in the table `loki_demo_logs` with the external labels `job` and `from`. For more information, please refer to the [Grafana Alloy loki.write documentation](https://grafana.com/docs/alloy/latest/reference/components/loki/loki.write/). You can run the following command to check the data in the table: ```sql SELECT * FROM loki_demo_logs; +----------------------------+------------------------+---------------------+--------------+-------+----------+ | greptime_timestamp | line | structured_metadata | filename | from | job | +----------------------------+------------------------+---------------------+--------------+-------+----------+ | 2024-11-25 11:02:31.256251 | Greptime is very cool! | {} | /tmp/foo.txt | alloy | greptime | +----------------------------+------------------------+---------------------+--------------+-------+----------+ 1 row in set (0.01 sec) ``` ## Data Model The Loki logs data model is mapped to the GreptimeDB data model according to the following rules: **Default table schema without external labels:** ```sql DESC loki_demo_logs; +---------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------------+---------------------+------+------+---------+---------------+ | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | line | String | | YES | | FIELD | | structured_metadata | Json | | YES | | FIELD | +---------------------+---------------------+------+------+---------+---------------+ 3 rows in set (0.00 sec) ``` - `greptime_timestamp`: The timestamp of the log entry - `line`: The log message content - `structured_metadata`: The structured metadata of the log entry in JSON format If the Loki Push request contains labels, they will be added as tags to the table schema (like `job` and `from` in the above example). **Important notes:** - All labels are treated as tags with string type - Do not attempt to pre-create the table using SQL to specify tag columns, as this will cause a type mismatch and write failure ### Example Table Schema The following is an example of the table schema with external labels: ```sql DESC loki_demo_logs; +---------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------------+---------------------+------+------+---------+---------------+ | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | line | String | | YES | | FIELD | | structured_metadata | Json | | YES | | FIELD | | filename | String | PRI | YES | | TAG | | from | String | PRI | YES | | TAG | | job | String | PRI | YES | | TAG | +---------------------+---------------------+------+------+---------+---------------+ 6 rows in set (0.00 sec) ``` ```sql SHOW CREATE TABLE loki_demo_logs\G *************************** 1. row *************************** Table: loki_demo_logs Create Table: CREATE TABLE IF NOT EXISTS `loki_demo_logs` ( `greptime_timestamp` TIMESTAMP(9) NOT NULL, `line` STRING NULL, `structured_metadata` JSON NULL, `filename` STRING NULL, `from` STRING NULL, `job` STRING NULL, TIME INDEX (`greptime_timestamp`), PRIMARY KEY (`filename`, `from`, `job`) ) ENGINE=mito WITH( 'comment' = 'Created on insertion', append_mode = 'true', 'greptime.semantic.signal_type' = 'log', 'greptime.semantic.source' = 'loki' ) 1 row in set (0.00 sec) ``` ## Using Pipeline with Loki Push API GreptimeDB supports using pipelines to process Loki push requests. You can simply set the HTTP header `x-greptime-pipeline-name` to the target pipeline name to enable pipeline processing. **Note:** When request data goes through the pipeline engine, GreptimeDB adds prefixes to the label and metadata column names: - `loki_label_` before each label name - `loki_metadata_` before each structured metadata name - The original Loki log line is named `loki_line` ### Pipeline Example Here is a complete example demonstrating how to use pipelines with Loki push API. **Step 1: Prepare the log file** Suppose we have a log file named `logs.json` with JSON-formatted log entries: ```json {"timestamp":"2025-08-21 14:23:17.892","logger":"sdk.tool.DatabaseUtil","level":"ERROR","message":"Connection timeout exceeded for database pool","trace_id":"a7f8c92d1e4b4c6f9d2e5a8b3f7c1d9e","source":"application"} {"timestamp":"2025-08-21 14:23:18.156","logger":"core.scheduler.TaskManager","level":"WARN","message":"Task queue capacity reached 85% threshold","trace_id":"b3e9f4a6c8d2e5f7a1b4c7d9e2f5a8b3","source":"scheduler"} {"timestamp":"2025-08-21 14:23:18.423","logger":"sdk.tool.NetworkUtil","level":"INFO","message":"Successfully established connection to remote endpoint","trace_id":"c5d8e7f2a9b4c6d8e1f4a7b9c2e5f8d1","source":"network"} ``` Each line is a separate JSON object containing log information. **Step 2: Create the pipeline configuration** Here is a pipeline configuration that parses the JSON log entries: ```yaml # pipeline.yaml version: 2 processors: - vrl: source: | message = parse_json!(.loki_line) target = { "log_time": parse_timestamp!(message.timestamp, "%Y-%m-%d %T%.3f"), "log_level": message.level, "log_source": message.source, "logger": message.logger, "message": message.message, "trace_id": message.trace_id, } . = target transform: - field: log_time type: time, ms index: timestamp ``` The pipeline content is straightforward: we use `vrl` processor to parse the line into a JSON object, then extract the fields to the root level. `log_time` is specified as the time index in the transform section, other fields will be auto-inferred by the pipeline engine, see [pipeline version 2](/reference/pipeline/pipeline-config.md#transform-in-version-2) for details. Note that the input field name is `loki_line`, which contains the original log line from Loki. **Step 3: Configure Grafana Alloy** Prepare an Alloy configuration file to read the log file and send it to GreptimeDB: ``` loki.source.file "greptime" { targets = [ {__path__ = "/logs.json"}, ] forward_to = [loki.write.greptime_loki.receiver] } loki.write "greptime_loki" { endpoint { url = "http://127.0.0.1:4000/v1/loki/api/v1/push" headers = { "x-greptime-pipeline-name" = "pp", } } external_labels = { "job" = "greptime", "from" = "alloy", } } ``` In the `greptime_loki`, the `x-greptime-pipeline-name` header is added to indicating the input data should be processed by the pipeline engine. **Step 4: Deploy and run** 1. First, start your GreptimeDB instance. See [here](/getting-started/installation/overview.md) for quick startup. 2. [Upload](/user-guide/logs/manage-pipelines.md#upload-a-pipeline) the pipeline configuration to the database using `curl`: ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/pp" -F "file=@pipeline.yaml" ``` 3. Start the Alloy Docker container to process the logs: ```shell docker run --rm \ -v ./config.alloy:/etc/alloy/config.alloy \ -v ./logs.json:/logs.json \ --network host \ grafana/alloy:latest \ run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data \ /etc/alloy/config.alloy ``` **Step 5: Verify the results** After the logs are processed, you can verify that they have been successfully ingested and parsed. The database logs will show the ingestion activity. Query the table using a MySQL client to see the parsed log data: ```sql mysql> show tables; +-----------+ | Tables | +-----------+ | loki_logs | | numbers | +-----------+ 2 rows in set (0.00 sec) mysql> select * from loki_logs limit 1 \G *************************** 1. row *************************** log_time: 2025-08-21 14:23:17.892000 log_level: ERROR log_source: application logger: sdk.tool.DatabaseUtil message: Connection timeout exceeded for database pool trace_id: a7f8c92d1e4b4c6f9d2e5a8b3f7c1d9e 1 row in set (0.01 sec) ``` This output demonstrates that the pipeline engine has successfully parsed the original JSON log lines and extracted the structured data into separate columns. For more details about pipeline configuration and features, refer to the [pipeline documentation](/reference/pipeline/pipeline-config.md). --- ## OpenTelemetry Protocol (OTLP) [OpenTelemetry](https://opentelemetry.io/) is a vendor-neutral open-source observability framework for instrumenting, generating, collecting, and exporting telemetry data such as traces, metrics, logs. The OpenTelemetry Protocol (OTLP) defines the encoding, transport, and delivery mechanism of telemetry data between telemetry sources, intermediate processes such as collectors and telemetry backends. ## OpenTelemetry Collectors You can easily configure GreptimeDB as the target for your OpenTelemetry collector. For more information, please refer to the [OTel Collector](otel-collector.md) and [Grafana Alloy](alloy.md) example. ## HTTP Base Endpoint [Base endpoint URL](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint) for all signal types: `http{s}:///v1/otlp` This unified endpoint is useful when sending multiple signal types (metrics, logs, and traces) to the same destination, simplifying your OpenTelemetry configuration. ## Metrics GreptimeDB is an observability backend to consume OpenTelemetry Metrics natively via [OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp) protocol. ### OTLP/HTTP API To send OpenTelemetry Metrics to GreptimeDB through OpenTelemetry SDK libraries, use the following information: - URL: `http{s}:///v1/otlp/v1/metrics` - Headers: - `X-Greptime-DB-Name`: `` - `Authorization`: `Basic` authentication, which is a Base64 encoded string of `:`. For more information, please refer to [Authentication](https://docs.greptime.com/user-guide/deployments-administration/authentication/static/) and [HTTP API](https://docs.greptime.com/user-guide/protocols/http#authentication) The request uses binary protobuf to encode the payload, so you need to use packages that support `HTTP/protobuf`. For example, in Node.js, you can use [`@opentelemetry/exporter-metrics-otlp-proto`](https://www.npmjs.com/package/@opentelemetry/exporter-metrics-otlp-proto); in Go, you can use [`go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp); in Java, you can use [`io.opentelemetry:opentelemetry-exporter-otlp`](https://mvnrepository.com/artifact/io.opentelemetry/opentelemetry-exporter-otlp); and in Python, you can use [`opentelemetry-exporter-otlp-proto-http`](https://pypi.org/project/opentelemetry-exporter-otlp-proto-http/). :::tip NOTE The package names may change according to OpenTelemetry, so we recommend that you refer to the official OpenTelemetry documentation for the most up-to-date information. ::: For more information about the OpenTelemetry SDK, please refer to the official documentation for your preferred programming language. ### Example Code Here are some example codes about how to setup the request in different languages: ```ts const auth = Buffer.from(`${username}:${password}`).toString('base64') const exporter = new OTLPMetricExporter({ url: `https://${dbHost}/v1/otlp/v1/metrics`, headers: { Authorization: `Basic ${auth}`, 'X-Greptime-DB-Name': db, }, timeoutMillis: 5000, }) ``` ```Go auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", *username, *password))) exporter, err := otlpmetrichttp.New( context.Background(), otlpmetrichttp.WithEndpoint(*dbHost), otlpmetrichttp.WithURLPath("/v1/otlp/v1/metrics"), otlpmetrichttp.WithHeaders(map[string]string{ "X-Greptime-DB-Name": *dbName, "Authorization": "Basic " + auth, }), otlpmetrichttp.WithTimeout(time.Second*5), ) ``` ```Java String endpoint = String.format("https://%s/v1/otlp/v1/metrics", dbHost); String auth = username + ":" + password; String b64Auth = new String(Base64.getEncoder().encode(auth.getBytes())); OtlpHttpMetricExporter exporter = OtlpHttpMetricExporter.builder() .setEndpoint(endpoint) .addHeader("X-Greptime-DB-Name", db) .addHeader("Authorization", String.format("Basic %s", b64Auth)) .setTimeout(Duration.ofSeconds(5)) .build(); ``` ```python auth = f"{username}:{password}" b64_auth = base64.b64encode(auth.encode()).decode("ascii") endpoint = f"https://{host}/v1/otlp/v1/metrics" exporter = OTLPMetricExporter( endpoint=endpoint, headers={"Authorization": f"Basic {b64_auth}", "X-Greptime-DB-Name": db}, timeout=5) ``` For more information on the example code, please refer to the official documentation for your preferred programming language. ### Prometheus Compatibility GreptimeDB supports a Prometheus-compatible mode for OTLP metrics ingestion. If the metrics data is persisted using the Prometheus-compatible format, you should be able to query them using PromQL, just like any Prometheus metrics. GreptimeDB selects the ingestion format once for each OTLP export request: - If none of the referenced metric tables exists, GreptimeDB uses the Prometheus-compatible format. - If all referenced existing tables use the same format, GreptimeDB uses that format for every metric in the request, including any tables created by the request. - If the request references existing tables in both formats, GreptimeDB rejects the request. Send legacy and Prometheus-compatible metrics in separate OTLP export requests. GreptimeDB pre-processes the incoming data before persisting them, including: 1. Converting the metric names(table names) and the label names to the Prometheus style(e.g: replace `.` with `_`). By default, GreptimeDB also adds Prometheus-style suffixes based on the metric unit and type. See [here](https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#metric-metadata-1) for details. Here are some examples of the conversion: | OTLP Metric / Attribute | OTLP Type / Unit | Prometheus Equivalent | | :--- | :--- | :--- | | `cache.hit_ratio` | Gauge / `1` | `cache_hit_ratio` | | `memory.usage` | Gauge / `By` | `memory_usage_bytes` | | `queue.length` | Gauge / `{item}` | `queue_length` | | `http.server.request.duration` | Histogram / `s` | `http_server_request_duration_seconds` | | `rpc.server.duration` | Histogram / `ms` | `rpc_server_duration_milliseconds` | | `http.client.request.size` | Sum (Monotonic) / `By` | `http_client_request_size_bytes_total` | | `system.network.io` | Sum (Monotonic) / `By` | `system_network_io_bytes_total` | | `http.status_code` (Attribute) | - | `http_status_code` | | `service.name` (Attribute) | - | `service_name` | 2. Discarding some resource attributes and all scope attributes by default. The kept resource attributes name list can be found [here](https://prometheus.io/docs/guides/opentelemetry/#promoting-resource-attributes). This behavior is configurable. Note, `Sum` and `Histogram` data in OTLP can have delta temporality. GreptimeDB saves their value directly without calculating the cumulative value. See [here](https://grafana.com/blog/2023/09/26/opentelemetry-metrics-a-guide-to-delta-vs.-cumulative-temporality-trade-offs/) for some context. You can set the HTTP headers to configure the pre-processing behaviors. Here are the options: 1. `x-greptime-otlp-metric-promote-all-resource-attrs`: Persist all resource attributes. Default to `false`. 2. `x-greptime-otlp-metric-promote-resource-attrs`: If not persisting all resource attributes, the attribute name list to be kept. Use `;` to join the name list. 3. `x-greptime-otlp-metric-ignore-resource-attrs`: If persisting all resource attributes, the attribute name list to be ignored. Use `;` to join the name list. 4. `x-greptime-otlp-metric-promote-scope-attrs`: Whether to persist the scope attributes. Default to `false`. 5. `x-greptime-otlp-metric-translation-strategy`: How to translate OTLP metric names(table names) and label names(tag columns) before persisting them. Default to `UnderscoreEscapingWithSuffixes`. The `x-greptime-otlp-metric-translation-strategy` header accepts the following values: | Value | Metric name behavior | Label name behavior | Original names | Translated names | | :--- | :--- | :--- | :--- | :--- | | `UnderscoreEscapingWithSuffixes` | Convert unsupported characters to `_`, and add Prometheus-style unit/type suffixes. | Convert unsupported characters to `_`. | Metric: `http.server.request-duration_total` (monotonic sum, unit `ms`)Label: `_http.status-code` | Metric: `http_server_request_duration_milliseconds_total`Label: `key_http_status_code` | | `UnderscoreEscapingWithoutSuffixes` | Convert unsupported characters to `_`, but do not add unit/type suffixes. | Convert unsupported characters to `_`. | Metric: `http.server.request-duration_total` (monotonic sum, unit `ms`)Label: `_http.status-code` | Metric: `http_server_request_duration_total`Label: `key_http_status_code` | | `NoUTF8EscapingWithSuffixes` | Keep the original metric name characters, and add Prometheus-style unit/type suffixes. | Keep the original label name characters. | Metric: `http.server.request-duration_total` (monotonic sum, unit `ms`)Label: `_http.status-code` | Metric: `http.server.request-duration_milliseconds_total`Label: `_http.status-code` | | `NoTranslation` | Keep the original metric name without adding suffixes. | Keep the original label name characters. | Metric: `http.server.request-duration_total` (monotonic sum, unit `ms`)Label: `_http.status-code` | Metric: `http.server.request-duration_total`Label: `_http.status-code` | The header value is case-sensitive. Invalid values are rejected with a `400 Bad Request` response. See [OTel specs](https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/prometheus/#translation-strategy) and [Prometheus docs](https://prometheus.io/docs/guides/opentelemetry/#utf-8) for more details. ### Data Model The Prometheus-compatible OTLP metrics data model is mapped to the GreptimeDB data model according to the following rules: - The name of the Metric will be used as the name of the GreptimeDB table, and the table will be automatically created if it does not exist. - Only selected resource attributes are kept by default. See above for details and configuration options. Attributes are used as tag columns in the GreptimeDB table. - You can refer to the [Prometheus Data Model](./prometheus.md#data-model) for other details. - ExponentialHistogram is not supported yet. If you are using a table that was created by an older OTLP metrics ingestion format, the existing table keeps that format. Here are some mapping differences: - All attributes, including resource attributes, scope attributes, and data point attributes, will be used as tag columns of the GreptimeDB table. - Each quantile of the Summary data type will be used as a separated data column of GreptimeDB, and the column name is `greptime_pxx`, where xx is the quantile, such as 90/99, etc. ## Logs GreptimeDB consumes OpenTelemetry Logs natively via [OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp) protocol. ### OTLP/HTTP API To send OpenTelemetry Logs to GreptimeDB through OpenTelemetry SDK libraries, use the following information: - URL: `http{s}:///v1/otlp/v1/logs` - Headers: - `X-Greptime-DB-Name`: `` - `Authorization`: `Basic` authentication, which is a Base64 encoded string of `:`. For more information, please refer to [Authentication](/user-guide/deployments-administration/authentication/static.md) and [HTTP API](/user-guide/protocols/http.md#authentication). - `X-Greptime-Log-Table-Name`: `` (optional) - The table name to store the logs. If not provided, the default table name is `opentelemetry_logs`. - `X-Greptime-Log-Extract-Keys`: `` (optional) - The keys to extract from the attributes. The keys should be separated by commas (`,`). For example, `key1,key2,key3` will extract the keys `key1`, `key2`, and `key3` from the attributes and promote them to the top level of the log, setting them as tags. Key matching is case-insensitive. If the same key exists in log attributes, scope attributes, and resource attributes, the value from log attributes takes precedence, followed by scope attributes and then resource attributes. Supported extracted value types are string, signed integer, unsigned integer, and boolean. If the field type is array, float, or object, an error will be returned. If a pipeline is provided, this setting will be ignored. - `X-Greptime-Pipeline-Name`: `` (optional) - The pipeline name to process the logs. If not provided, GreptimeDB uses the built-in OTLP log mapping and applies `X-Greptime-Log-Extract-Keys` if it is provided. - `X-Greptime-Pipeline-Version`: `` (optional) - The pipeline version to process the logs. If not provided, the latest version of the pipeline will be used. - `X-Greptime-Pipeline-Params`: `` (optional) - The pipeline parameters to use when processing logs with a custom pipeline. `X-Greptime-Log-Pipeline-Name` and `X-Greptime-Log-Pipeline-Version` are also accepted as legacy aliases for the generic pipeline headers. Use `X-Greptime-Pipeline-Name` and `X-Greptime-Pipeline-Version` for new configurations. The request uses binary protobuf to encode the payload, so you need to use packages that support `HTTP/protobuf`. :::tip NOTE The package names may change according to OpenTelemetry, so we recommend that you refer to the official OpenTelemetry documentation for the most up-to-date information. ::: For more information about the OpenTelemetry SDK, please refer to the official documentation for your preferred programming language. ### Example Code Please refer to the sample code in the [OpenTelemetry Collector documentation](otel-collector.md), which includes how to send OpenTelemetry logs to GreptimeDB. You can also refer to the sample code in the [Alloy documentation](alloy.md#logs) to learn how to send OpenTelemetry logs to GreptimeDB. ### Custom Pipeline Input When `X-Greptime-Pipeline-Name` is provided, GreptimeDB converts each OTLP log record to one pipeline event. The event uses the following fields: | Field | Description | | --- | --- | | `Timestamp` | The OTLP `time_unix_nano`. | | `ObservedTimestamp` | The OTLP `observed_time_unix_nano`. | | `TraceId`, `SpanId` | Hex-encoded IDs. | | `TraceFlags`, `SeverityText`, `SeverityNumber`, `Body` | The corresponding OTLP log fields. `Body` is converted to a string. | | `ResourceSchemaUrl`, `ScopeSchemaUrl`, `ScopeName`, `ScopeVersion` | The corresponding resource and scope fields. | | `ResourceAttributes`, `ScopeAttributes`, `LogAttributes` | Objects containing the corresponding OTLP attributes. | ### Data Model The OTLP logs data model is mapped to the GreptimeDB data model according to the following rules: Default table schema: ```sql +-----------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------------+---------------------+------+------+---------+---------------+ | timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | trace_id | String | | YES | | FIELD | | span_id | String | | YES | | FIELD | | severity_text | String | | YES | | FIELD | | severity_number | Int32 | | YES | | FIELD | | body | String | | YES | | FIELD | | log_attributes | Json | | YES | | FIELD | | trace_flags | UInt32 | | YES | | FIELD | | scope_name | String | PRI | YES | | TAG | | scope_version | String | | YES | | FIELD | | scope_attributes | Json | | YES | | FIELD | | scope_schema_url | String | | YES | | FIELD | | resource_attributes | Json | | YES | | FIELD | | resource_schema_url | String | | YES | | FIELD | +-----------------------+---------------------+------+------+---------+---------------+ 14 rows in set (0.00 sec) ``` - You can use `X-Greptime-Log-Table-Name` to specify the table name for storing the logs. If not provided, the default table name is `opentelemetry_logs`. - All attributes, including resource attributes, scope attributes, and log attributes, will be stored as a JSON column in the GreptimeDB table. - The `body` column is created with a fulltext index by default. The index is enabled with the default fulltext settings: `analyzer=English`, `case_sensitive=false`, and `backend=bloom`. For the Bloom backend, the default `granularity` is `10240` and the default `false_positive_rate` is `0.01`. For details, see the [fulltext index documentation](/user-guide/manage-data/data-index.md#full-text-index). - The timestamp of the log will be used as the timestamp index in GreptimeDB, with the column name `timestamp`. It is preferred to use `time_unix_nano` as the timestamp column. If `time_unix_nano` is not provided, `observed_time_unix_nano` will be used instead. ### Append Only By default, log table created by OpenTelemetry API are in [append only mode](/user-guide/deployments-administration/performance-tuning/design-table.md#when-to-use-append-only-tables). ## Traces GreptimeDB supports writing OpenTelemetry traces data directly via the [OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp) protocol, and it also provides a table model of OpenTelemetry traces for users to query and analyze traces data conveniently. ### OTLP/HTTP API You can use [OpenTelemetry SDK](https://opentelemetry.io/docs/languages/) or other similar technologies to add traces data to your application. You can also use [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) to collect traces data and use GreptimeDB as the backend storage. To send OpenTelemetry traces data to GreptimeDB through OpenTelemetry SDK libraries, please use the following information: - URL: `http{s}:///v1/otlp/v1/traces` - Headers: - `Content-Type`: `application/x-protobuf` - `Authorization`: `Basic` authentication. - `X-Greptime-DB-Name`: `` - `X-Greptime-Trace-Table-Name`: `` (optional) - The table name to store the traces. If not provided, the default table name is `opentelemetry_traces`. - `X-Greptime-Pipeline-Name`: `greptime_trace_v1` (required) - The pipeline name to process the traces. GreptimeDB accepts **protobuf encoded traces data** via **HTTP protocol**. ### Example Code You can directly send OpenTelemetry traces data to GreptimeDB, or use OpenTelemetry Collector to collect traces data and use GreptimeDB as the backend storage. Please refer to the example code in the [OpenTelemetry Collector documentation](/user-guide/traces/read-write.md#opentelemetry-collector) to learn how to send OpenTelemetry traces data to GreptimeDB. ### Data Model GreptimeDB maps the OTLP traces data model to a table schema. By default, trace data is stored in the `opentelemetry_traces` table. ```sql +------------------------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------------------------+---------------------+------+------+---------+---------------+ | timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | timestamp_end | TimestampNanosecond | | YES | | FIELD | | duration_nano | UInt64 | | YES | | FIELD | | parent_span_id | String | | YES | | FIELD | | trace_id | String | | YES | | FIELD | | span_id | String | | YES | | FIELD | | span_kind | String | | YES | | FIELD | | span_name | String | | YES | | FIELD | | span_status_code | String | | YES | | FIELD | | span_status_message | String | | YES | | FIELD | | trace_state | String | | YES | | FIELD | | scope_name | String | | YES | | FIELD | | scope_version | String | | YES | | FIELD | | service_name | String | PRI | YES | | TAG | | span_attributes.net.sock.peer.addr | String | | YES | | FIELD | | span_attributes.peer.service | String | | YES | | FIELD | | span_events | Json | | YES | | FIELD | | span_links | Json | | YES | | FIELD | +------------------------------------+---------------------+------+------+---------+---------------+ ``` - Each row represents a single span. - `service_name` is used as a **Tag** (part of the **Primary Key**). - `timestamp` is used as the **Time Index**. - Resource attributes, scope attributes, and span attributes are automatically flattened into separate columns. - Note: `resource_attributes.service.name` is excluded from flattening as it is already stored in the `service_name` column. - `span_events` and `span_links` are stored as `JSON` data types by default. For more details on the data model and auxiliary tables, please refer to [Trace Data Modeling](/user-guide/traces/data-model.md). Note: 1. The `greptime_trace_v1` process uses the `trace_id` field to divide data into partitions for better performance. **Please make sure the first letter of the `trace_id` is evenly distributed**. 2. For non-test scenarios, you might want to set a `ttl` to the trace table to avoid data overload. Set the HTTP header `x-greptime-hints: ttl=7d` would set a `ttl` of 7 days during the table creation, see [here](/reference/sql/create.md#table-options) for more details about `ttl` in table option. ### Schema Evolution and Partial Success The `greptime_trace_v1` model automatically adds new flattened attribute columns. When a trace table already exists, GreptimeDB reconciles incoming attribute value types with the existing table schema. Compatible scalar values can be coerced to the existing column type, and existing `Int64` attribute columns may be widened to `Float64` when incoming values contain both integers and floats. If some spans in a trace request cannot be written because of incompatible schema or values, GreptimeDB may still accept the other spans and return an OTLP `partial_success` response with `rejected_spans` and `error_message`. If all spans are rejected, the endpoint returns a `400 Bad Request` response. ### Auxiliary Tables GreptimeDB automatically creates auxiliary tables (e.g., `opentelemetry_traces_services` and `opentelemetry_traces_operations`) to facilitate searching for services and operations. See [Auxiliary Tables](/user-guide/traces/data-model.md#auxiliary-tables) for details. ### Append-only Mode By default, trace tables created by the OpenTelemetry API are in [append-only mode](/user-guide/deployments-administration/performance-tuning/design-table.md#when-to-use-append-only-tables). --- ## OTel Collector [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) offers a vendor-agnostic implementation of how to receive, process and export telemetry data. It can act as an intermediate layer to send data from different sources to GreptimeDB. Below is a sample configuration for sending data to GreptimeDB using OpenTelemetry Collector. ```yaml extensions: basicauth/client: client_auth: username: password: receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 exporters: otlp_http/traces: endpoint: 'http://127.0.0.1:4000/v1/otlp' # auth: # authenticator: basicauth/client headers: # x-greptime-db-name: '' x-greptime-pipeline-name: 'greptime_trace_v1' tls: insecure: true otlp_http/logs: endpoint: 'http://127.0.0.1:4000/v1/otlp' # auth: # authenticator: basicauth/client headers: # x-greptime-db-name: '' # x-greptime-log-table-name: '' # x-greptime-pipeline-name: '' # x-greptime-pipeline-params: '' tls: insecure: true otlp_http/metrics: endpoint: 'http://127.0.0.1:4000/v1/otlp' # auth: # authenticator: basicauth/client headers: # x-greptime-db-name: '' tls: insecure: true service: # extensions: [basicauth/client] pipelines: traces: receivers: [otlp] exporters: [otlp_http/traces] logs: receivers: [otlp] exporters: [otlp_http/logs] metrics: receivers: [otlp] exporters: [otlp_http/metrics] ``` In the above configuration, we define a receiver `otlp` that can receive data from OpenTelemetry. We also define three exporters: `otlp_http/traces`, `otlp_http/logs`, and `otlp_http/metrics`, which send data to the OTLP endpoint of GreptimeDB. Based on the OTLP/HTTP protocol, we have added some headers to specify certain parameters, such as `x-greptime-pipeline-name` and `x-greptime-log-table-name`: * The `x-greptime-pipeline-name` header is used to specify the pipeline name to use, and, * the `x-greptime-pipeline-params` header is used to pass parameters to the pipeline, and, * the `x-greptime-log-table-name` header is used to specify the table name in GreptimeDB where the data will be written. If you have enabled [authentication](/user-guide/deployments-administration/authentication/overview.md) in GreptimeDB, you need to use the `basicauth/client` extension to handle basic authentication. Here, we strongly recommend using different exporters to handle traces, logs, and metrics separately. On one hand, GreptimeDB supports some specific headers to customize processing flows; on the other hand, this also helps with data isolation. For more about OpenTelemetry protocols, please read the [doc](/user-guide/ingest-data/for-observability/opentelemetry.md). --- ## Ingest Data for Observability Observability data reaches GreptimeDB through the protocol its source already speaks. [Ingest Data](../overview.md) lists every source in one table; this page groups the observability paths by signal. ## Metrics - [Prometheus Remote Write](prometheus.md) — for an existing Prometheus server or Prometheus Agent. - [OpenTelemetry](opentelemetry.md) — OTLP/HTTP from an OpenTelemetry SDK. - [InfluxDB Line Protocol](../for-iot/influxdb-line-protocol.md) — the path for migrating off InfluxDB, and for Telegraf. This page also covers the Telegraf configuration. ## Logs - [Loki](loki.md) — the Loki Push API, the path for migrating off Loki without changing the clients already writing to it. This page also covers the Alloy configuration. - [Elasticsearch](elasticsearch.md) — the `_bulk` API, compatible with Logstash, Filebeat, and Telegraf. - [Splunk](splunk.md) — the HTTP Event Collector (HEC) protocol, compatible with Vector and the OpenTelemetry Collector. Parsing and transforming text logs before they are stored is covered in [Logs](/user-guide/logs/overview.md). ## Traces - [OpenTelemetry](opentelemetry.md) — OTLP/HTTP from an SDK or the Collector. Storing traces and querying them with SQL or the Jaeger-compatible API is covered in [Traces](/user-guide/traces/overview.md). ## Agents and pipelines These sit between the source and GreptimeDB, writing through an exporter, sink, or output that you configure. The signals each one carries differ: - [OpenTelemetry Collector](otel-collector.md) — routes metrics, logs, and traces through an OTLP/HTTP exporter. - [Vector](vector.md) — metrics through the `greptimedb_metrics` sink, logs through `greptimedb_logs`. No traces sink. - [Fluent Bit](fluent-bit.md) — logs through the HTTP output; metrics, logs, and traces through the OpenTelemetry output; metrics also through Prometheus Remote Write. - [Grafana Alloy](alloy.md) — writes through Remote Write, OpenTelemetry, or Loki. - [Kafka](kafka.md) — consumed into GreptimeDB with Vector as the transport. --- ## Prometheus GreptimeDB can serve as a long-term storage solution for Prometheus, providing a seamless integration experience. ## Remote write configuration ### Prometheus configuration file To configure Prometheus with GreptimeDB, update your [Prometheus configuration file](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file) (`prometheus.yml`) as follows: ```yaml remote_write: - url: http://localhost:4000/v1/prometheus/write?db=public # Uncomment and set credentials if authentication is enabled # basic_auth: # username: greptime_user # password: greptime_pwd remote_read: - url: http://localhost:4000/v1/prometheus/read?db=public # Uncomment and set credentials if authentication is enabled # basic_auth: # username: greptime_user # password: greptime_pwd ``` - The host and port in the URL represent the GreptimeDB server. In this example, the server is running on `localhost:4000`. You can replace it with your own server address. For the HTTP protocol configuration in GreptimeDB, please refer to the [protocol options](/user-guide/deployments-administration/configuration.md#protocol-options). - The `db` parameter in the URL represents the database to which we want to write data. It is optional. By default, the database is set to `public`. - `basic_auth` is the authentication configuration. Fill in the username and password if GreptimeDB authentication is enabled. Please refer to the [authentication document](/user-guide/deployments-administration/authentication/overview.md). ### Grafana Alloy configuration file If you are using Grafana Alloy, configure the remote write endpoint in the Alloy configuration file (`config.alloy`). For more information, refer to the [Alloy documentation](alloy.md#prometheus-remote-write). ### Vector configuration file If you use Vector, configure Remote Write in Vector's configuration file (vector.toml). For more information, see the [Vector documentation](vector.md#using-prometheus-remote-write-protocol). ## Data Model In the [data model](/user-guide/concepts/data-model.md) of GreptimeDB, data is organized into tables with columns for tags, time index, and fields. GreptimeDB can be thought of as a multi-value data model, automatically grouping multiple Prometheus metrics into corresponding tables. This allows for efficient data management and querying. ![Data Model](/PromQL-multi-value-data-model.png) When the metrics are written into GreptimeDB by remote write endpoint, they will be transformed as follows: | Sample Metrics | In GreptimeDB | GreptimeDB Data Types | | -------------- | ------------------------- | --------------------- | | Name | Table (Auto-created) Name | String | | Value | Column (Field) | Double | | Timestamp | Column (Time Index) | Timestamp | | Label | Column (Tag) | String | For example, the following Prometheus metric: ```txt prometheus_remote_storage_samples_total{instance="localhost:9090", job="prometheus", remote_name="648f0c", url="http://localhost:4000/v1/prometheus/write"} 500 ``` will be transformed as a row in the table `prometheus_remote_storage_samples_total`: | Column | Value | Column Data Type | | :----------------- | :------------------------------------------ | :----------------- | | instance | localhost:9090 | String | | job | prometheus | String | | remote_name | 648f0c | String | | url | `http://localhost:4000/v1/prometheus/write` | String | | greptime_value | 500 | Double | | greptime_timestamp | The sample's unix timestamp | Timestamp | ## Improve efficiency by using metric engine The Prometheus remote writing always creates a large number of small tables. These tables are classified as logical tables in GreptimeDB. However, having a large number of small tables can be inefficient for both data storage and query performance. To address this, GreptimeDB introduces the [metric engine](/contributor-guide/datanode/metric-engine.md) feature, which stores the data represented by the logical tables in a single physical table. This approach reduces storage overhead and improves columnar compression efficiency. The metric engine is enabled by default in GreptimeDB, and you don't need to specify any additional configuration. By default, the physical table used is `greptime_physical_table`. If you want to use a specific physical table, you can specify the `physical_table` parameter in the remote write URL. If the specified physical table doesn't exist, it will be automatically created. ```yaml remote_write: - url: http://localhost:4000/v1/prometheus/write?db=public&physical_table=greptime_physical_table ``` Data is stored in the physical table, while queries are performed on logical tables to provide an intuitive view from a metric perspective. For instance, when successfully writing data, you can use the following command to display the logical tables: ```sql show tables; ``` ```sql +---------------------------------------------------------------+ | Tables | +---------------------------------------------------------------+ | prometheus_remote_storage_enqueue_retries_total | | prometheus_remote_storage_exemplars_pending | | prometheus_remote_storage_read_request_duration_seconds_count | | prometheus_rule_group_duration_seconds | | ...... | +---------------------------------------------------------------+ ``` The physical table itself can also be queried. It contains columns from all the logical tables, making it convenient for multi-join analysis and computation. To view the schema of the physical table, use the `DESC TABLE` command: ```sql DESC TABLE greptime_physical_table; ``` The physical table includes all the columns from the logical tables: ```sql +--------------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+----------------------+------+------+---------+---------------+ | greptime_timestamp | TimestampMillisecond | PRI | NO | | TIMESTAMP | | greptime_value | Float64 | | YES | | FIELD | | __table_id | UInt32 | PRI | NO | | TAG | | __tsid | UInt64 | PRI | NO | | TAG | | device | String | PRI | YES | | TAG | | instance | String | PRI | YES | | TAG | | job | String | PRI | YES | | TAG | | error | String | PRI | YES | | TAG | ... ``` You can use the `SELECT` statement to filter data from the physical table as needed. For example, you can filter data based on the `device` condition from logical table A and the `job` condition from logical table B: ```sql SELECT * FROM greptime_physical_table WHERE greptime_timestamp > '2024-08-07 03:27:26.964000' AND device = 'device1' AND job = 'job1'; ``` ### GreptimeDB cluster with metric engine If you are using GreptimeDB cluster for Prometheus remote write, you may notice that only 1 datanode taking all the workload and other datanodes receives no traffic. This is because with default settings, there is only 1 metric engine physical table, and only 1 partition(region) in that table. The datanode that serves the partition will take all the data ingestion. Why we are not creating more partitions by default? [GreptimeDB's table partition](/user-guide/deployments-administration/manage-data/table-sharding.md) is based pre-configured partition columns. However, in Prometheus ecosystem, there is no common column (label, as in Prometheus) that is suitable for good partition rules. To fix this, we recommend you to define your own partition rule based on your data model. For example, it can be `namespace` if your are monitoring a kubernetes cluster. The partition columns should have enough cardinality to divide data. Also we recommend you to create 2x-3x partitions on initial datanode count, so when you scaling more datanodes in your cluster, just migrate those partitions to new ones. An example DDL of partitioned physical table based on `namespace` label: ```sql CREATE TABLE greptime_physical_table ( greptime_timestamp TIMESTAMP(3) NOT NULL, greptime_value DOUBLE NULL, namespace STRING PRIMARY KEY, TIME INDEX (greptime_timestamp), ) PARTITION ON COLUMNS (namespace) ( namespace <'g', namespace >= 'g' AND namespace < 'n', namespace >= 'n' AND namespace < 't', namespace >= 't' ) ENGINE = metric with ( "physical_metric_table" = "", ); ``` Note that you won't have need add all possible *PRIMARY KEY* (label) here, metric engine will add them automatically. Only labels you use for partitioning are required to be defined ahead of time. ## Special labels for ingestion options :::warning Experimental Feature This experimental feature may contain unexpected behavior, have its functionality change in the future. ::: Normally, the complete dataset of a remote write request is ingested into the database under the same option, for example, a default physical table with metric engine enabled. All the logical tables (i.e, the metrics) is backed with the same physical table, even when the number of metrics grows. It's probably fine for data ingestion. However, this set-up might slow down the query speed if you just want to query for a small group of metrics, but the database have to scan the complete dataset because they are all in the same physical table. If you can foresee a large data volume and incremental queries upon a small group of metrics each time, then it might be useful to split the storage during the ingestion to reduce the query overhead later. This fine-grade level of control can be achieved using ingest options for each metric within a remote request. GreptimeDB supports special labels. These labels (along with their values) turn into ingest options during the parsing phase, allowing each individual metric within a request to be more precisely controlled. The labels are not mutually exclusive, so they can be combined together to produce more versatile controlling. Here is a representative diagram of special labels for a metric. Note this is not the actual data model of a metric. | `__name__` | `x_greptime_database` | `x_greptime_physical_table` | `pod_name_label` | `__normal_label_with_underscore_prefix__` | `timestamp` | `value` | |--------------------|-------------------------|-------------------------------|-----------------------|---------------------------------------------|---------------------------|-----------| | `some_metric_name` | `public` | `p_1` | `random_k8s_pod_name` | `true` | `2025-06-17 16:31:52.000` | `12.1` | The special labels you see above are just normal valid labels in Prometheus. GreptimeDB recognizes some of the label names and turns them into ingest options. It's much like the custom HTTP headers, where you just set a valid HTTP header and its value to indicate following operations, only the header pair means nothing outside your program. Here is a list of supported label names: - `x_greptime_database` - `x_greptime_physical_table` ### Setting labels How to set labels to the metrics is very dependent on the tools (or code) that collects the metrics and send them over to the database. If you're using Prometheus to scrape metrics from the source and send them to GreptimeDB using remote write, you can add `external_labels` in the global config. Refer to the [docs](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file) here. It's the same for other collecting tools. You may have to find the relevant configuration for your tool of choice. ### `x_greptime_database` This option decides which database the metric goes into. Note, the database should be created in advance(for instance, using `create database xxx` SQL). Metrics from the same tech stack can have same metric names. For example, if you have two Kubernetes clusters with very different content, and you deploy a single collector on both clusters, they will generate metrics with same names but with different labels or values. If the metrics are collected and ingested into the same database, then on a Grafana dashboard you will have to manually set every label selection on every diagram to view the two clusters' metrics separately. This can be very tedious and painful. In this case, you might want to store the metrics on different databases during ingestion and use two dashboards to view the metrics separately. ### `x_greptime_physical_table` If the metric is storing using the metric engine, then there is a physical table behind each metric's logical table. By default, all metrics using the same physical table. With the number of the metrics growing, this physical table becomes a super wide table. If the metric frequency is different, then the table will be sparse. Finding a certain metric or a certain label in the complete metrics dataset would be very time-consuming since the database have to scan all the 'irrelevant' data. In this case, you might want to split the metrics into different physical tables to ease the pressure on a single physical table. It can also be helpful to group metrics by their frequency. Note, each metric's logical table is bound to a physical table upon creation. So setting different physical table for the same metric within the same database won't work. ## Using pipeline in remote write GreptimeDB supports using a pipeline to process Prometheus remote write requests. You can simply set the HTTP header `x-greptime-pipeline-name` to the target pipeline name to enable pipeline processing. Here is a very simple pipeline configuration, using `vrl` processor to add a `source` label to each metric: ```YAML version: 2 processors: - vrl: source: | .source = "local_laptop" . transform: - field: greptime_timestamp type: time, ms index: timestamp ``` The result looks something like this ``` mysql> select * from `go_memstats_mcache_inuse_bytes`; +----------------------------+----------------+--------------------+---------------+--------------+ | greptime_timestamp | greptime_value | instance | job | source | +----------------------------+----------------+--------------------+---------------+--------------+ | 2025-07-11 07:42:03.064000 | 1200 | node_exporter:9100 | node-exporter | local_laptop | | 2025-07-11 07:42:18.069000 | 1200 | node_exporter:9100 | node-exporter | local_laptop | +----------------------------+----------------+--------------------+---------------+--------------+ 2 rows in set (0.01 sec) ``` You can refer to the [pipeline's documentation](/user-guide/logs/use-custom-pipelines.md) for more details. ## Performance tuning ### Batching mode When the metric engine is enabled, GreptimeDB supports a batching mode for Prometheus Remote Write that reduces per-request overhead and improves ingestion throughput. In batching mode, incoming rows are accumulated and flushed to the metric engine in larger batches, which can yield up to **2x throughput improvement** in clustered deployments with multiple regions, but requires careful tuning of batch parameters to match the ingestion workload and prevent increased latency. Batching mode is **disabled by default**. To enable it, set `pending_rows_flush_interval` to a non-zero duration in the `[prom_store]` section of the configuration file: ```toml [prom_store] enable = true with_metric_engine = true pending_rows_flush_interval = "500ms" ``` The following table describes the batching-related options: | Key | Type | Default | Description | | ---------------------------- | ------- | -------- | --------------------------------------------------------------------------- | | pending_rows_flush_interval | String | `"0s"` | Interval between batch flushes. `"0s"` disables batching. | | max_batch_rows | Integer | `100000` | Maximum number of rows per batch before a flush is triggered. | | max_concurrent_flushes | Integer | `256` | Maximum number of flush operations that can run concurrently. | | max_inflight_requests | Integer | `3000` | Maximum number of in-flight write requests waiting for batch completion. | :::tip Batching mode only takes effect when both `with_metric_engine` is `true` and `pending_rows_flush_interval` is set to a non-zero duration. ::: ### Request timeout and retries When [`http.timeout`](/user-guide/deployments-administration/configuration.md#protocol-options) is set to a non-zero duration and a remote write request to `/v1/prometheus/write` exceeds it, GreptimeDB responds with `504 Gateway Timeout` instead of `408 Request Timeout`. Prometheus and other remote write senders retry on `5xx` responses, so a timed-out request is retried automatically instead of being dropped. In batching mode, rows that have been accepted into a pending batch continue to flush in the background even after the request times out. To ensure a request can wait long enough for its batch to flush, GreptimeDB raises a non-zero `http.timeout` that is less than or equal to `pending_rows_flush_interval` plus 1 second to that value and logs a warning. Setting `http.timeout = "0s"` (the default) disables the HTTP timeout entirely. ### Customized physical table By default, the metric engine will automatically create a physical table named `greptime_physical_table` if it does not already exist. For performance optimization, you may choose to create a physical table with customized configurations. ### Enable skipping index By default, the metric engine won't create indexes for columns. You can enable it by setting the `index.type` to `skipping`. Create a physical table with a skipping index; all automatically added columns will have this index applied. ```sql CREATE TABLE greptime_physical_table ( greptime_timestamp TIMESTAMP(3) NOT NULL, greptime_value DOUBLE NULL, TIME INDEX (greptime_timestamp), ) engine = metric with ( "physical_metric_table" = "", "index.type" = "skipping" ); ``` For more configurations, please refer to the [create table](/reference/sql/create.md#create-table) section. ## VictoriaMetrics remote write VictoriaMetrics slightly modified Prometheus remote write protocol for better compression. The protocol is automatically enabled when you are using `vmagent` to send data to a compatible backend. GreptimeDB has this variant supported, too. Just configure GreptimeDB's remote write url for `vmagent`. For example, if you have GreptimeDB installed locally: ```shell vmagent -remoteWrite.url=http://localhost:4000/v1/prometheus/write ``` --- ## Splunk ## Overview GreptimeDB implements a subset of the [Splunk HTTP Event Collector (HEC)](https://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector) protocol, so shippers that already speak HEC — such as Vector and the OpenTelemetry Collector — can write to GreptimeDB by changing the endpoint URL and the token. Two ingestion endpoints are available: - `/services/collector/event` takes structured JSON events. - `/services/collector/raw` takes plain text and stores it verbatim. Splunk's `index` maps to a GreptimeDB table. `host`, `source`, `sourcetype` and the keys under `fields` become tag columns, which GreptimeDB adds to the table's primary key. Indexer acknowledgment (`/services/collector/ack`) is not implemented. The `channel` parameter is accepted and ignored. :::tip NOTE Fluent Bit's native `splunk` output cannot be used here. It hardcodes the request path to `/services/collector/event` and exposes no path setting, so it cannot reach GreptimeDB's `/v1/splunk` mount without a reverse proxy. To send data from Fluent Bit, use [its HTTP output](/user-guide/ingest-data/for-observability/fluent-bit.md) instead. ::: ## HTTP API The base path is `/v1/splunk`; clients append the collector path themselves. Point your shipper at `http://${db_host}:${db_http_port}/v1/splunk`, for example `http://localhost:4000/v1/splunk`. | Endpoint | Method | Purpose | | --- | --- | --- | | `/v1/splunk/services/collector/event` | `POST` | Ingest JSON events. Aliases: `/services/collector`, `/services/collector/event/1.0` | | `/v1/splunk/services/collector/raw` | `POST` | Ingest plain text. Alias: `/services/collector/raw/1.0` | | `/v1/splunk/services/collector/health` | `GET` | Health probe. Alias: `/services/collector/health/1.0` | Request bodies may be gzip-compressed (`Content-Encoding: gzip`). ### Authentication Authentication only applies when the server runs with a [user provider](/user-guide/deployments-administration/authentication/overview.md). Without one, requests are accepted unauthenticated. HEC clients send `Authorization: Splunk `. GreptimeDB reads the token as `username:password`: ``` Authorization: Splunk greptime_user:greptime_pwd ``` `Authorization: Basic ` is also accepted. :::warning Most Splunk clients default to a single opaque token with no colon in it. GreptimeDB rejects such a token with HEC code `4`. Configure the client's token as `:`. ::: The health endpoint is public and never requires a token. ## Ingest JSON events `POST /v1/splunk/services/collector/event` The body is one or more HEC event objects. They may be concatenated directly, separated by JSON whitespace such as a newline or a space, or wrapped in a top-level JSON array. Any other separator, a comma or a semicolon for example, is a parse error and fails the request with HEC code `6`. ### Field mapping | HEC field | Maps to | | --- | --- | | `time` | The `greptime_timestamp` time index | | `index` | The table name | | `host`, `source`, `sourcetype` | Tag columns | | keys under `fields` | Tag columns, one per key | | `event` and any remaining keys | Data columns | `time` is epoch seconds and may be fractional. Values at or above `1e12` are read as milliseconds instead. If `time` is absent or `null`, the server's ingest time is used. :::warning A `time` that is present but unparsable is an error, not a fallback — the request fails with HEC code `6`. The same applies to `?time=` on the raw endpoint. ::: ### Table routing The table is resolved in this order: 1. The event's `index`, coerced into a valid table name. Characters outside `[A-Za-z0-9_:\-.@#]` become `_`, and an underscore is prepended when the first character is not valid. For example `my index/2026` becomes `my_index_2026`. 2. The `table` query parameter. 3. The default table `splunk_logs`. ### Query parameters | Parameter | Description | | --- | --- | | `db` | Target database. Defaults to `public`. | | `table` | Fallback table name, used when the event carries no `index`. | | `pipeline_name` | Pipeline to run. Defaults to the built-in `greptime_identity`. | | `version` | Pipeline version. Defaults to the latest version. | :::tip NOTE Only these parameters are honored on this endpoint. Other log-ingestion parameters such as `msg_field`, `custom_time_index`, and `ignore_errors` have no effect here. ::: ### Example ```bash curl -X POST "http://localhost:4000/v1/splunk/services/collector/event" \ -H "Authorization: Splunk greptime_user:greptime_pwd" \ -H "Content-Type: application/json" \ -d '{"time":1785312000,"host":"web-01","source":"/var/log/nginx/access.log","sourcetype":"nginx:access","index":"splunk_events","fields":{"env":"prod","region":"us-west"},"event":{"status":200,"path":"/api/v1/health","latency_ms":12}} {"time":1785312001,"host":"web-02","source":"/var/log/nginx/access.log","sourcetype":"nginx:access","index":"splunk_events","fields":{"env":"prod","region":"eu-central"},"event":{"status":503,"path":"/api/v1/orders","latency_ms":841}}' ``` A successful write returns: ```json {"code":0,"text":"Success"} ``` The table `splunk_events` is created on the first write: ```sql DESC TABLE splunk_events; ``` ``` +--------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+---------------------+------+------+---------+---------------+ | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | env | String | PRI | YES | | TAG | | event.latency_ms | Int64 | | YES | | FIELD | | event.path | String | | YES | | FIELD | | event.status | Int64 | | YES | | FIELD | | host | String | PRI | YES | | TAG | | region | String | PRI | YES | | TAG | | source | String | PRI | YES | | TAG | | sourcetype | String | PRI | YES | | TAG | +--------------------+---------------------+------+------+---------+---------------+ ``` The default pipeline flattens the nested `event` object, which is why the data columns are named `event.status`, `event.path`, and `event.latency_ms`. ```sql SELECT * FROM splunk_events ORDER BY greptime_timestamp; ``` ``` +---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+ | greptime_timestamp | env | event.latency_ms | event.path | event.status | host | region | source | sourcetype | +---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+ | 2026-07-29 08:00:00 | prod | 12 | /api/v1/health | 200 | web-01 | us-west | /var/log/nginx/access.log | nginx:access | | 2026-07-29 08:00:01 | prod | 841 | /api/v1/orders | 503 | web-02 | eu-central | /var/log/nginx/access.log | nginx:access | +---------------------+------+------------------+----------------+--------------+--------+------------+---------------------------+--------------+ ``` New keys appearing under `fields` on a later write are added as tag columns and joined to the primary key automatically. ## Ingest raw text `POST /v1/splunk/services/collector/raw` The body is stored verbatim in a `message` column. GreptimeDB does not parse its content — use a [custom pipeline](#using-a-pipeline) reading `message` if you need structure. :::warning By default the **entire body is one event**, so multi-line payloads such as stack traces stay intact. To split the body into several events, pass `linebreaker` with the delimiter you want, percent-encoded — `?linebreaker=%0A` splits on newlines. Whitespace-only segments are dropped. ::: Metadata is request-level on this endpoint: it comes from query parameters and applies to every event in the request. ### Query parameters | Parameter | Description | | --- | --- | | `host`, `source`, `sourcetype` | Tag columns applied to every event in the request. | | `index` | Table name, sanitized the same way as the `index` field on `/event`. | | `time` | Epoch timestamp applied to every event. Defaults to ingest time. | | `table` | Fallback table name when `index` is absent. | | `linebreaker` | Percent-encoded literal delimiter used to split the body. Without it, the whole body is one event. | | `pipeline_name` | Pipeline to run. Defaults to `greptime_identity`. | | `version` | Pipeline version. | | `db` | Target database. Defaults to `public`. | | `channel` | Accepted and ignored. Also accepted as the `x-splunk-request-channel` header. | ### Example ```bash printf 'line one\nline two\nline three\n' | curl -X POST \ "http://localhost:4000/v1/splunk/services/collector/raw?index=splunk_raw&host=web-01&source=app.log&sourcetype=applog&linebreaker=%0A" \ -H "Authorization: Splunk greptime_user:greptime_pwd" \ --data-binary @- ``` This writes three rows. Dropping `linebreaker` from the same request writes one row containing all three lines. ```sql SHOW CREATE TABLE splunk_raw; ``` ```sql CREATE TABLE IF NOT EXISTS `splunk_raw` ( `greptime_timestamp` TIMESTAMP(9) NOT NULL, `host` STRING NULL, `message` STRING NULL, `source` STRING NULL, `sourcetype` STRING NULL, TIME INDEX (`greptime_timestamp`), PRIMARY KEY (`host`, `source`, `sourcetype`) ) ENGINE=mito WITH( 'comment' = 'Created on insertion', append_mode = 'true' ) ``` ## Using a pipeline Both endpoints run every event through a [pipeline](/user-guide/logs/manage-pipelines.md). The default is the built-in `greptime_identity`, which maps each input key to a column of the same name. Override it with the `pipeline_name` query parameter or the `x-greptime-pipeline-name` header. When both are present, the query parameter wins. Use `version` to pin a specific pipeline version. :::warning Promoting `host`, `source`, `sourcetype`, and the `fields` keys to tag columns only happens on the default `greptime_identity` path. A custom pipeline owns its output schema, so declare the columns you want indexed in the pipeline itself. ::: ## Response codes The response body is `{"text": ..., "code": ...}`, matching HEC. Clients branch on `code`. | Code | Meaning | | --- | --- | | `0` | Success | | `2` | Token is required | | `4` | Invalid token | | `5` | No data | | `6` | Invalid data format, including an unparsable `time` or `version` | | `7` | Incorrect index, meaning the resolved table name is invalid | | `8` | Internal server error | | `12` | Event field is required | | `13` | Event field cannot be blank | | `17` | Returned by the health endpoint to report a healthy collector | :::warning Validation rejects the whole request. If any event in a batch has a missing or blank `event`, or a `time` that is present but unparsable, none of the events in that batch are written — the endpoint does not skip bad events and keep the rest. Optional metadata such as `host` or `source` may be absent without any effect. ::: ## Vector Vector's [`splunk_hec_logs`](https://vector.dev/docs/reference/configuration/sinks/splunk_hec_logs/) sink writes to GreptimeDB with no other changes: ```toml [sinks.greptime] type = "splunk_hec_logs" inputs = ["my_source"] endpoint = "http://localhost:4000/v1/splunk" default_token = "greptime_user:greptime_pwd" index = "vector_logs" compression = "gzip" encoding.codec = "json" ``` - `endpoint`: the `/v1/splunk` base path, without a collector path. Vector appends that itself. - `default_token`: must be `:`, not an opaque token. - `index`: the target table. - `encoding.codec`: required by this sink. - `endpoint_target`: `event` by default, which uses `/services/collector/event`. Set it to `raw` for the raw endpoint. To use the raw endpoint instead: ```toml [sinks.greptime] type = "splunk_hec_logs" inputs = ["my_source"] endpoint = "http://localhost:4000/v1/splunk" default_token = "greptime_user:greptime_pwd" index = "vector_raw_logs" endpoint_target = "raw" encoding.codec = "text" batch.max_events = 1 ``` Vector concatenates batched events with no separator, so on the raw endpoint a whole batch arrives as one event. Set `batch.max_events = 1` as above, or stay on the default `event` target. :::tip NOTE `linebreaker` would split such a batch server-side, but this particular sink cannot send it: it has no option for arbitrary query parameters, and appending one to `endpoint` does not work because Vector joins `endpoint` and the collector path as plain strings — `http://localhost:4000/v1/splunk?linebreaker=%0A` becomes `/v1/splunk?linebreaker=%0A/services/collector/raw?index=...`, which no longer matches the route. HEC clients that can set query parameters use `linebreaker` normally. ::: ## OpenTelemetry Collector The [`splunk_hec` exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/splunkhecexporter/README.md) is configured the same way: ```yaml exporters: splunk_hec: endpoint: "http://localhost:4000/v1/splunk/services/collector" token: "greptime_user:greptime_pwd" index: "otel_logs" tls: insecure: true service: pipelines: logs: receivers: [otlp] exporters: [splunk_hec] ``` Unlike Vector, this exporter expects the full collector path in `endpoint`. --- ## Vector :::tip NOTE This document is based on Vector v0.49.0. All example configurations below are based on this version. Please adjust the host and port configurations for each sink according to your actual GreptimeDB instance. All port values below are defaults. ::: Vector is a high-performance observability data pipeline. It natively supports GreptimeDB as a metrics data receiver. Through Vector, you can receive metrics data from various sources including Prometheus, OpenTelemetry, StatsD, etc. GreptimeDB can serve as a sink component for Vector to receive metrics data. ## Writing Metrics Data GreptimeDB supports multiple ways to write metrics data: - Using [`greptimedb_metrics` sink](https://vector.dev/docs/reference/configuration/sinks/greptimedb_metrics/) - Using InfluxDB line protocol format - Using Prometheus Remote Write protocol ### Using `greptimedb_metrics` sink #### Example Below is an example configuration using `greptimedb_metrics` sink to write host metrics: ```toml # sample.toml [sources.in] type = "host_metrics" [sinks.my_sink_id] inputs = ["in"] type = "greptimedb_metrics" endpoint = ":4001" dbname = "" username = "" password = "" new_naming = true ``` Vector uses gRPC to communicate with GreptimeDB, so the default port for Vector sink is `4001`. If you changed the default gRPC port when starting GreptimeDB with [custom configuration](/user-guide/deployments-administration/configuration.md#configuration-file-options), please use your own port. For more requirements, please visit [Vector GreptimeDB Configuration](https://vector.dev/docs/reference/configuration/sinks/greptimedb_metrics/) to view more configuration options. ### Data Model The following rules are used when storing Vector metrics into GreptimeDB: - Use `_` as the table name in GreptimeDB, for example, `host_cpu_seconds_total`; - Use the timestamp of the metric as the time index of GreptimeDB, the column name is `ts`; - Use the tags of the metric as GreptimeDB tags; - For Vector metrics which have multiple subtypes: - For Counter and Gauge metrics, the values are stored in the `val` column; - For Set metrics, the number of data points are stored in the `val` column; - For Distribution metrics, the values of each percentile are stored in the `pxx` column, where xx is the percentile, and the `min/max/avg/sum/count` columns are also stored; - For AggregatedHistogram metrics, the values of each bucket are stored in the `bxx` column, where xx is the upper limit of the bucket, and the `sum/count` columns are also stored; - For AggregatedSummary metrics, the values of each percentile are stored in the `pxx` column, where xx is the percentile, and the `sum/count` columns are also stored; - For Sketch metrics, the values of each percentile are stored in the `pxx` column, where xx is the percentile, and the `min/max/avg/sum` columns are also stored; ### Using InfluxDB Line Protocol Format You can use the `influx` sink to write metrics data. We recommend using v2 version of InfluxDB line protocol format. Below is an example configuration using `influx` sink to write host metrics: ```toml # sample.toml [sources.my_source_id] type = "internal_metrics" [sinks.my_sink_id] type = "influxdb_metrics" inputs = [ "my_source_id" ] bucket = "public" endpoint = "http://:4000/v1/influxdb" org = "" token = "" ``` The above configuration uses v2 version of InfluxDB line protocol. Vector determines the InfluxDB protocol version based on fields in the TOML configuration, so please ensure the configuration contains `bucket`, `org`, and `token` fields. Specific field explanations: - `type`: Value for InfluxDB line protocol is `influxdb_metrics`. - `bucket`: Database name in GreptimeDB. - `org`: Organization name in GreptimeDB (needs to be empty). - `token`: Token for authentication (needs to be empty). Since Influx line protocol token has special format and must start with `Token `, this differs from GreptimeDB's authentication method and is currently not compatible. If using GreptimeDB instance with authentication, please use `greptimedb_metrics`. For more details, please refer to [InfluxDB Line Protocol documentation](../for-iot/influxdb-line-protocol.md) to learn how to write data to GreptimeDB using InfluxDB Line Protocol. ### Using Prometheus Remote Write Protocol Below is an example configuration using Prometheus Remote Write protocol to write host metrics: ```toml # sample.toml [sources.my_source_id] type = "internal_metrics" [sinks.prometheus_remote_write] type = "prometheus_remote_write" inputs = [ "my_source_id" ] endpoint = "http://:4000/v1/prometheus/write?db=" compression = "snappy" auth = { strategy = "basic", username = "", password = "" } ``` ## Writing Logs Data GreptimeDB supports multiple ways to write logs data: - Using [`greptimedb_logs` sink](https://vector.dev/docs/reference/configuration/sinks/greptimedb_logs/) to write logs data to GreptimeDB. - Using Loki protocol to write logs data to GreptimeDB. We strongly recommend all users to use `greptimedb_logs` sink to write logs data, as it is optimized for GreptimeDB and better supports GreptimeDB features. We also recommend enabling compression for various protocols to improve data transmission efficiency. ### Using `greptimedb_logs` sink (recommended) ```toml # sample.toml [sources.my_source_id] type = "demo_logs" count = 10 format = "apache_common" interval = 1 [sinks.my_sink_id] type = "greptimedb_logs" inputs = [ "my_source_id" ] compression = "gzip" dbname = "public" endpoint = "http://:4000" extra_headers = { "x-greptime-pipeline-params" = "max_nested_levels=10" } pipeline_name = "greptime_identity" table = "" username = "" password = "" [sinks.my_sink_id.extra_params] source = "vector" skip_error = "true" ``` This example demonstrates how to use `greptimedb_logs` sink to write generated demo logs data to GreptimeDB. For more information, please refer to [Vector greptimedb_logs sink](https://vector.dev/docs/reference/configuration/sinks/greptimedb_logs/) documentation. ### Using Loki Protocol #### Example ```toml [sources.generate_syslog] type = "demo_logs" format = "syslog" count = 100 interval = 1 [transforms.remap_syslog] inputs = ["generate_syslog"] type = "remap" source = """ .labels = { "host": .host, "service": .service, } .structured_metadata = { "source_type": .source_type } """ [sinks.my_sink_id] type = "loki" inputs = ["remap_syslog"] compression = "snappy" endpoint = "http://:4000" out_of_order_action = "accept" path = "/v1/loki/api/v1/push" encoding = { codec = "raw_message" } labels = { "*" = "{{labels}}" } structured_metadata = { "*" = "{{structured_metadata}}" } auth = {strategy = "basic", user = "", password = ""} ``` The above configuration writes logs data to GreptimeDB using Loki protocol. Specific configuration item explanations: - `compression`: Sets compression algorithm for data transmission, using `snappy` here. - `endpoint`: Specifies Loki's receiving address. - `out_of_order_action`: Sets how to handle out-of-order logs, choosing `accept` here to accept out-of-order logs. GreptimeDB supports writing out-of-order logs. - `path`: Specifies Loki's API path. - `encoding`: Sets data encoding method, using `raw_message` here. - `labels`: Specifies log labels, mapping `labels` content to `{{labels}}` here. That is the `labels` field in remap_syslog. - `structured_metadata`: Specifies structured metadata, mapping `structured_metadata` content to `{{structured_metadata}}` here. That is the `structured_metadata` field in remap_syslog. For meanings of `labels` and `structured_metadata`, please refer to [Loki documentation](https://grafana.com/docs/loki/latest/get-started/labels/bp-labels/). For Loki protocol, `labels` will use Tag type in time series scenarios by default, please avoid using high-cardinality fields for these fields. `structured_metadata` will be stored as a complete JSON field. Note that since Vector's Loki sink configuration doesn't allow setting headers, you cannot specify a custom GreptimeDB log table or pipeline through `X-Greptime-*` headers. Logs are written to GreptimeDB's default Loki table, `loki_logs`. If you need to specify a table or use pipeline functionality, please consider using the `greptimedb_logs` sink. --- ## Ingest Data GreptimeDB accepts data through established observability and database protocols, so most sources write to it without a custom client. This page maps each source to the protocol it writes with and the guide that covers it. ## Choose an Ingestion Path | Data comes from | Writes with | Guide | | --- | --- | --- | | OpenTelemetry SDK | OTLP/HTTP | [OpenTelemetry Protocol (OTLP)](./for-observability/opentelemetry.md) | | OpenTelemetry Collector | OTLP/HTTP exporter | [OTel Collector](./for-observability/otel-collector.md) | | Prometheus | Remote Write | [Prometheus](./for-observability/prometheus.md) | | Grafana Alloy | Remote Write, OTLP, or Loki | [Grafana Alloy](./for-observability/alloy.md) | | Loki clients | Loki Push API | [Loki](./for-observability/loki.md) | | Elasticsearch clients | Bulk API | [Elasticsearch](./for-observability/elasticsearch.md) | | Splunk shippers | HTTP Event Collector (HEC) | [Splunk](./for-observability/splunk.md) | | Vector | `greptimedb_metrics` or `greptimedb_logs` sink | [Vector](./for-observability/vector.md) | | Fluent Bit | HTTP output for logs, OpenTelemetry output for all three signals | [Fluent Bit](./for-observability/fluent-bit.md) | | Kafka topics | Vector as the transport | [Kafka](./for-observability/kafka.md) | | Telegraf or InfluxDB clients | InfluxDB line protocol | [InfluxDB Line Protocol](./for-iot/influxdb-line-protocol.md) | | OpenTSDB clients | `/v1/opentsdb/api/put` | [OpenTSDB](./for-iot/opentsdb.md) | | MQTT devices | EMQX data integration | [EMQX](./for-iot/emqx.md) | | Your own application | gRPC SDK | [gRPC SDKs](./for-iot/grpc-sdks/overview.md) | | Existing SQL tooling | MySQL or PostgreSQL protocol | [SQL](./for-iot/sql.md) | | Flink jobs | Flink SQL, the Table API, or the DataStream API | [Apache Flink](/user-guide/integrations/flink.md) | | Spark jobs | Batch DataFrames or Structured Streaming micro-batches | [Apache Spark](/user-guide/integrations/spark.md) | The same guides are also grouped by scenario, in [observability sources](./for-observability/overview.md) and [IoT sources](./for-iot/overview.md). Text logs and traces need more than a write protocol. See [Logs](/user-guide/logs/overview.md) for parsing and transforming logs with pipelines, and [Traces](/user-guide/traces/overview.md) for storing and querying OTLP traces. ## Automatic Schema Generation GreptimeDB creates the table and adds columns as data arrives, so no table has to be defined in advance. All protocols and integrations support this except [SQL](./for-iot/sql.md), [Apache Flink](/user-guide/integrations/flink.md), and [Apache Spark](/user-guide/integrations/spark.md). ## Next Steps - [Query Data](/user-guide/query-data/overview.md) — SQL, PromQL, and the Jaeger-compatible API. - [Update, Delete, and TTL](/user-guide/manage-data/overview.md) — updating by overwrite, deleting, and expiring data. --- ## Grafana Alloy(Integrations) GreptimeDB can be set up as a data sink for Grafana Alloy. For more information, please refer to the [Ingest Data through Grafana Alloy](/user-guide/ingest-data/for-observability/alloy.md) guide. --- ## Coroot Coroot is an open-source APM and observability tool, an alternative to DataDog and New Relic. It covers metrics, logs, traces, continuous profiling, and SLO-based alerting. GreptimeDB can be configured as a Prometheus data sink for Coroot. To integrate GreptimeDB with Coroot, navigate to `Settings` in the Coroot Dashboard, select the `Prometheus` configuration, and enter the following information: - Prometheus URL: `http{s}:///v1/prometheus` - If you have [enabled authentication](/user-guide/deployments-administration/authentication/static.md) on GreptimeDB, check the HTTP basic auth option and enter GreptimeDB username and password. Otherwise, leave it unchecked. - Remote Write URL: `http{s}:///v1/prometheus/write?db=` ## Example Configuration If your GreptimeDB host is `localhost` with port `4000` for the HTTP service and authentication is enabled, and you want to use the default database `public`, use the following configuration: - Prometheus URL: `http://localhost:4000/v1/prometheus` - Enable the HTTP basic auth option and enter GreptimeDB username and password - Remote Write URL: `http://localhost:4000/v1/prometheus/write?db=public` The image below shows the Coroot configuration example: Once the configuration is saved successfully, you can begin using Coroot to monitor your instances. The image below shows an example of a Coroot dashboard using GreptimeDB as a data source: ![coroot-cpu](/coroot-cpu.png) --- ## DBeaver [DBeaver](https://dbeaver.io/) is a free, open-source, cross-platform database tool. DBeaver 26.1.5 and later include a dedicated GreptimeDB driver that connects over the MySQL protocol. ## Create a connection Click the "New Database Connection" button in the DBeaver toolbar, select GreptimeDB from the database list, and click "Next". The connection settings page is prefilled with GreptimeDB defaults: - Connect by: Host - Host: `localhost` if GreptimeDB is running on your local machine - Port: `4002` if you use the default GreptimeDB configuration - Database/Schema: `public`, you can use any other database name you have created - Enter the username and password if authentication is enabled on GreptimeDB; otherwise, leave them blank. ![GreptimeDB connection settings in DBeaver](/dbeaver/connection-settings.png) Click "Test Connection" to verify the connection settings and click "Finish" to save the connection. ## Browse data The database navigator lists `public`, `information_schema`, `greptime_private`, and the tables in each. Open a table to browse its rows on the "Data" tab, or run queries from the SQL editor. The "Properties" tab shows each column's name and data type, along with its GreptimeDB semantic type: `TIMESTAMP`, `TAG`, or `FIELD`. ![Browsing a table in DBeaver](/dbeaver/browse-data.png) ## Use the MySQL driver On DBeaver versions earlier than 26.1.5, select MySQL instead of GreptimeDB and install the MySQL driver if you haven't already. The connection details are the same, except that you must enter the host, port, and database name yourself. Either driver only covers what the MySQL protocol exposes. For more information on interacting with GreptimeDB using MySQL, refer to the [MySQL protocol documentation](/user-guide/protocols/mysql.md). --- ## EMQX(Integrations) GreptimeDB can be used as a data system for EMQX. For more information, please refer to the [Ingest Data through EMQX](/user-guide/ingest-data/for-iot/emqx.md) guide. --- ## Apache Flink The [GreptimeDB connector for Apache Flink](https://github.com/GreptimeTeam/flink-connector-greptimedb) supports: - Writing insert-only records through Flink SQL, the Table API, or the DataStream API. - Reading a GreptimeDB table as a bounded source through Flink SQL or the Table API. The connector requires Java 17 and Apache Flink 2.0.x. ## Install the connector ### Download from Maven Central For a Maven-based Flink application, add the connector as a dependency: ```xml io.greptime flink-connector-greptimedb 0.1.0 ``` For Flink SQL Client or a Flink cluster, download the shaded JAR from [Maven Central](https://central.sonatype.com/artifact/io.greptime/flink-connector-greptimedb/0.1.0): ```bash mvn dependency:copy \ -Dartifact=io.greptime:flink-connector-greptimedb:0.1.0:jar:shaded \ -DoutputDirectory=/path/to/flink/lib ``` ### Build from source Build the connector from source: ```bash git clone https://github.com/GreptimeTeam/flink-connector-greptimedb.git cd flink-connector-greptimedb mvn package ``` The build creates a shaded JAR under `target/`. Load it when starting the Flink SQL Client: ```bash ./bin/sql-client.sh embedded \ -j /path/to/flink-connector-greptimedb-*-shaded.jar ``` ## Write data with Flink SQL Create the destination table in GreptimeDB before writing data: ```sql CREATE TABLE cpu_metrics ( ts TIMESTAMP(3) TIME INDEX, host STRING, usage DOUBLE, PRIMARY KEY (host) ); ``` Then create a Flink table that uses the GreptimeDB connector: ```sql CREATE TEMPORARY TABLE cpu_metrics_sink ( ts TIMESTAMP(3) NOT NULL, host STRING, usage DOUBLE ) WITH ( 'connector' = 'greptimedb', 'endpoints' = '127.0.0.1:4001', 'database' = 'public', 'table' = 'cpu_metrics', 'time-index' = 'ts', 'tags' = 'host', 'batch.max-rows' = '1000' ); INSERT INTO cpu_metrics_sink VALUES (TIMESTAMP '2024-01-02 03:04:05.000', 'host-a', 0.42); ``` `endpoints` uses the GreptimeDB gRPC endpoint, whose default port is `4001`. The `time-index` column must be declared as a non-null `TIMESTAMP` or `TIMESTAMP_LTZ` column. Columns listed in `tags` should match the primary key columns of the destination GreptimeDB table. The commonly used sink options are: | Option | Required | Default | Description | | --- | --- | --- | --- | | `connector` | Yes | - | Must be `greptimedb`. | | `endpoints` | Yes | - | Comma-separated GreptimeDB gRPC endpoints. | | `time-index` | Yes | - | Non-null timestamp column used as the GreptimeDB time index. | | `database` | No | `public` | Destination database. | | `table` | No | Flink table name | Destination table. | | `username` | No | - | GreptimeDB username. Configure with `password`. | | `password` | No | - | GreptimeDB password. Configure with `username`. | | `tags` | No | - | Comma-separated columns written as GreptimeDB tags. | | `batch.max-rows` | No | `1000` | Maximum number of rows in each write batch. | The sink provides at-least-once delivery. Failed writes, retries, or recovery can produce duplicate writes. It does not support update, delete, or retract rows, primary-key upserts, automatic table creation, or exactly-once commits. For DataStream applications, use `GreptimeSink` and provide a `GreptimeRecordSerializer`. See the [connector README](https://github.com/GreptimeTeam/flink-connector-greptimedb#datastream-sink-usage) for the Java API example and advanced bulk-write options. ## Read data with Flink SQL The Table source reads an existing GreptimeDB table through the MySQL protocol. Add MySQL Connector/J to the Flink runtime together with the GreptimeDB connector: ```bash ./bin/sql-client.sh embedded \ -j /path/to/flink-connector-greptimedb-*-shaded.jar \ -j /path/to/mysql-connector-j-8.4.0.jar ``` Create the source table with a schema that matches the GreptimeDB table: ```sql CREATE TEMPORARY TABLE cpu_metrics_source ( ts TIMESTAMP(3), host STRING, usage DOUBLE ) WITH ( 'connector' = 'greptimedb', 'query.jdbc-url' = 'jdbc:mysql://127.0.0.1:4002/public?useSSL=false', 'database' = 'public', 'table' = 'cpu_metrics', 'query.fetch-size' = '1000' ); SELECT ts, host, usage FROM cpu_metrics_source; ``` Use the separate `username` and `password` options when authentication is enabled. Do not put credentials or timeout parameters in `query.jdbc-url`. The source performs a bounded, single-task scan. It does not support streaming reads, CDC, lookup reads, or schema discovery. The connector does not push projections, filters, or limits down to GreptimeDB; Flink applies these operations after reading the data. For all source options and supported data types, see the [connector README](https://github.com/GreptimeTeam/flink-connector-greptimedb#sqltable-source-options). --- ## Fluent Bit(Integrations) You can set GreptimeDB as an output for Fluent Bit. For more information, please refer to the [Writing Data with Fluent Bit](/user-guide/ingest-data/for-observability/fluent-bit.md) guide. --- ## Grafana GreptimeDB can be configured as a [Grafana data source](https://grafana.com/docs/grafana/latest/datasources/add-a-data-source/). You have the option to connect GreptimeDB with Grafana using one of three data sources: [GreptimeDB](#greptimedb-data-source-plugin), [Prometheus](#prometheus-data-source), or [MySQL](#mysql-data-source). ## GreptimeDB data source plugin The [GreptimeDB data source plugin](https://github.com/GreptimeTeam/greptimedb-grafana-datasource) is based on the ClickHouse data source and adds GreptimeDB-specific features. The plugin adapts perfectly to the GreptimeDB data model, thus providing a better user experience. In addition, it also solves some compatibility issues compared to using the Prometheus data source directly. ### Installation The plugin is not published in the Grafana plugin catalog, so install the unsigned archive and allow it explicitly in `grafana.ini`: ```ini [plugins] allow_loading_unsigned_plugins = info8fcc-greptimedb-datasource ``` `GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS` is the equivalent environment variable. Grafana Cloud does not accept unsigned plugins, so use a self-hosted Grafana. If you need a signed build bound to your own Grafana `root_url`, [contact us](https://greptime.com/contactus). Make sure Grafana is installed and running before installing the plugin. You can choose one of the following installation methods: - Download `info8fcc-greptimedb-datasource-unsigned.zip` from the [release page](https://github.com/GreptimeTeam/greptimedb-grafana-datasource/releases/latest/) and unzip it to your [grafana plugin directory](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#plugins). - Use grafana cli to download and install: ```shell grafana cli --pluginUrl https://github.com/GreptimeTeam/greptimedb-grafana-datasource/releases/latest/download/info8fcc-greptimedb-datasource-unsigned.zip plugins install info8fcc ``` - Use our [prebuilt Grafana docker image](https://hub.docker.com/r/greptime/grafana-greptimedb), which ships the plugin by default: `docker run -p 3000:3000 greptime/grafana-greptimedb:latest` Note that you may need to restart your grafana server after installing the plugin. ### Connection settings Click the Add data source button and select GreptimeDB as the type. ![grafana-add-greptimedb-data-source](/grafana-add-greptimedb-data-source.png) Fill in the following URL in the GreptimeDB server URL: ```txt http://:4000 ``` In the Auth section, click basic auth, and fill in the username and password for GreptimeDB in the Basic Auth Details section (not set by default, no need to fill in). - User: `` - Password: `` Then click the Save & Test button to test the connection. ### General Query Settings Before selecting any query type, you first need to configure the **Database** and **Table** to query from. | Setting | Description | | :-------- | :---------------------------------------- | | **Database** | Select the database you want to query. | | **Table** | Select the table you want to query from. | ![DB Table Config](/grafana/dbtable.png) --- ### Table Query Choose the `Table` query type when your query results **do not include a time column**. This is suitable for displaying tabular data. | Setting | Description | | :-------- | :---------------------------------------------- | | **Columns** | Select the columns you want to retrieve. Multiple selections are allowed. | | **Filters** | Set conditions to filter your data. | ![Table Query](/grafana/table.png) --- ### Metrics Query Select the `Time Series` query type when your query results **include both a time column and a numerical value column**. This is ideal for visualizing metrics over time. | Main Setting | Description | | :----------- | :-------------------- | | **Time** | Select the time column. | | **Columns** | Select the numerical value column(s). | ![Time Series](/grafana/series1.png) --- ### Logs Query Choose the `Logs` query type when you want to query log data. You'll need to specify a **Time** column and a **Message** column. | Main Setting | Description | | :----------- | :---------------------------- | | **Time** | Select the timestamp column for your logs. | | **Message** | Select the column containing the log content. | | **Log Level**| (Optional) Select the column representing the log level. | ![Logs](/grafana/logs.png) #### logs Context Query Logs Context Query Performs an approximate time range query based on the value of context columns in a log row. * First, set the context column in Connection Page. ![Context Config](/grafana/context2.png) * Then, when making a query, include the context column in the query. ![Query Config](/grafana/context1.png) --- ### Traces Query Select the `Traces` query type when you want to query distributed tracing data. | Main Setting | Description | | :-------------------- | :------------------------------------------------------------------------------------------------------ | | **Trace Model** | Select `Trace Search` to query a list of traces. | | **Trace Id Column** | Default value: `trace_id` | | **Span Id Column** | Default value: `span_id` | | **Parent Span ID Column** | Default value: `parent_span_id` | | **Service Name Column** | Default value: `service_name` | | **Operation Name Column** | Default value: `span_name` | | **Start Time Column** | Default value: `timestamp` | | **Duration Time Column** | Default value: `duration_nano` | | **Duration Unit** | Default value: `nanoseconds` | | **Tags Column** | Multiple selections allowed. Corresponds to columns starting with `span_attributes` (e.g., `span_attributes.http.method`). | | **Service Tags Column** | Multiple selections allowed. Corresponds to columns starting with `resource_attributes` (e.g., `resource_attributes.host.name`). | ![Traces](/grafana/traceconfig.png) ## Prometheus data source Click the "Add data source" button and select Prometheus as the type. Fill in Prometheus server URL in HTTP: ```txt http://:4000/v1/prometheus ``` Click basic auth in the Auth section and fill in your GreptimeDB username and password in Basic Auth Details: - User: `` - Password: `` Click Custom HTTP Headers and add one header: - Header: `x-greptime-db-name` - Value: `` Then click "Save & Test" button to test the connection. For how to query data with PromQL, please refer to the [Prometheus Query Language](/user-guide/query-data/promql.md) document. ## MySQL data source Click the "Add data source" button and select MySQL as the type. Fill in the following information in MySQL Connection: - Host: `:4002` - Database: `` - User: `` - Password: `` - Session timezone: `UTC` Then click "Save & Test" button to test the connection. Note that you need to use raw SQL editor for panel creation. SQL Builder is not supported due to timestamp data type difference between GreptimeDB and vanilla MySQL. For how to query data with SQL, please refer to the [Query Data with SQL](/user-guide/query-data/sql.md) document. --- ## Kafka(Integrations) Vector can be used as a tool to transport data from Kafka to GreptimeDB. For more information, please refer to the [Ingest Data via Kafka](/user-guide/ingest-data/for-observability/kafka.md) guide. --- ## Model Context Protocol (MCP) :::warning Experimental Feature The GreptimeDB MCP Server is currently in experimental stage and under active development. APIs and features may change without notice. Please use with caution in production environments. ::: The [GreptimeDB MCP Server](https://github.com/GreptimeTeam/greptimedb-mcp-server) provides a Model Context Protocol implementation that enables AI assistants like Claude to securely query and analyze your GreptimeDB databases using SQL, TQL (PromQL-compatible), and RANGE queries — with a read-only SQL gate and data masking built in. Watch our [demo video on YouTube](https://www.youtube.com/watch?v=EBTc46yamFI) to see the MCP Server in action. ## What is MCP? Model Context Protocol (MCP) is a standard protocol that lets AI assistants interact with external data sources and tools. With the GreptimeDB MCP Server, an assistant can explore your tables, understand what each one represents through the [semantic layer](/user-guide/concepts/semantic-layer.md), run queries, and manage pipelines and dashboards. ## Tools The server (v0.5.0) exposes the following tools. ### Query and inspection | Tool | Description | | --- | --- | | `execute_sql` | Execute SQL queries with format (csv/json/markdown) and limit options. | | `execute_tql` | Execute TQL (PromQL-compatible) queries for time-series analysis. | | `query_range` | Execute time-window aggregation queries with RANGE/ALIGN syntax. | | `describe_table` | Inspect a table profile: schema, semantic metadata, latest sample rows, and query guidance. | | `explain_query` | Analyze SQL or TQL query execution plans. | | `health_check` | Check database connection status and server version. | `describe_table` reads the [table semantic layer](/user-guide/concepts/semantic-layer.md), so the assistant learns the signal type, source, and metric metadata of a table instead of guessing from column names. ### Pipeline management | Tool | Description | | --- | --- | | `list_pipelines` | List all pipelines or get details of a specific pipeline. | | `create_pipeline` | Create a new pipeline with YAML configuration. | | `dryrun_pipeline` | Test a pipeline with sample data without writing to the database. | | `delete_pipeline` | Delete a specific version of a pipeline. | ### Dashboard management | Tool | Description | | --- | --- | | `list_dashboards` | List all Perses dashboard definitions. | | `create_dashboard` | Create or update a Perses dashboard definition. | | `delete_dashboard` | Delete a dashboard definition. | The server also ships built-in Jinja prompt templates for common tasks (metrics analysis, trace analysis, pipeline creation, schema design, query performance tuning, and more). ## Installation ```bash pip install greptimedb-mcp-server ``` Run it directly (connects to `localhost:4002` by default): ```bash greptimedb-mcp-server --host localhost --database public ``` ## Configuration The server is configured through environment variables or CLI arguments. The table below lists the most common options; [HTTP server mode](#http-server-mode) adds a few more. | Environment variable | CLI argument | Default | Description | | --- | --- | --- | --- | | `GREPTIMEDB_HOST` | `--host` | `localhost` | Database host. | | `GREPTIMEDB_PORT` | `--port` | `4002` | MySQL protocol port. | | `GREPTIMEDB_USER` | `--user` | `root` | Database user. | | `GREPTIMEDB_PASSWORD` | `--password` | — | Database password. | | `GREPTIMEDB_DATABASE` | `--database` | `public` | Database name. | | `GREPTIMEDB_TIMEZONE` | `--timezone` | `UTC` | Session timezone. | | `GREPTIMEDB_HTTP_PORT` | `--http-port` | `4000` | HTTP API port (for pipeline/dashboard tools). | | `GREPTIMEDB_MASK_ENABLED` | `--mask-enabled` | `true` | Mask sensitive columns. | | `GREPTIMEDB_AUDIT_ENABLED` | `--audit-enabled` | `true` | Log every tool invocation. | | `GREPTIMEDB_ALLOW_WRITE` | `--allow-write` | `false` | Allow write/DDL via `execute_sql`. See [Write mode](#write-mode) below. | | `GREPTIMEDB_TRANSPORT` | `--transport` | `stdio` | Transport: `stdio`, `sse`, or `streamable-http`. | ### Claude Desktop integration Add the following to your `claude_desktop_config.json`: ```json { "mcpServers": { "greptimedb": { "command": "greptimedb-mcp-server", "args": ["--host", "localhost", "--database", "public"], "env": { "GREPTIMEDB_PORT": "4002", "GREPTIMEDB_USER": "your_username", "GREPTIMEDB_PASSWORD": "your_password" } } } } ``` ### HTTP server mode For containerized or Kubernetes deployments, run the server over HTTP instead of stdio (requires `mcp>=1.8.0`): ```bash # Streamable HTTP (recommended for production) greptimedb-mcp-server --transport streamable-http --listen-port 8080 # SSE mode (legacy) greptimedb-mcp-server --transport sse --listen-port 3000 ``` DNS rebinding protection is disabled by default for compatibility with proxies and Kubernetes services. Enable it with `--allowed-hosts` if needed. ## Security `execute_sql` is **read-only by default**, and the server applies several safeguards. The read-only guarantee covers `execute_sql` only. The pipeline and dashboard tools (`create_pipeline`, `delete_pipeline`, `create_dashboard`, `delete_dashboard`) reach GreptimeDB through the HTTP API and modify those resources regardless of `--allow-write`. If the assistant must not change anything, restrict it at the database level rather than relying on this flag. - **Security gate**: blocks `DROP`, `DELETE`, `TRUNCATE`, `UPDATE`, `INSERT`, `ALTER`, `CREATE`, `GRANT`, `REVOKE`, and encoded bypass attempts; allows `SELECT`, `SHOW`, `DESCRIBE`, `TQL`, `EXPLAIN`, `UNION`. - **Data masking**: columns whose names match patterns such as `password`, `token`, `api_key`, `ssn`, or `credit_card` are masked as `******`. Add patterns with `--mask-patterns`. - **Audit logging**: every tool invocation is logged. Disable with `--audit-enabled false`. For the strongest setup, also create a read-only database user with the [static user provider](/user-guide/deployments-administration/authentication/static.md) and connect the server with it. ### Write mode For local development or testing, you can allow write/DDL statements (`CREATE`, `DROP`, `ALTER`, `INSERT`, `UPDATE`, `DELETE`) through `execute_sql`: ```bash greptimedb-mcp-server --allow-write true ``` When enabled, the security gate is bypassed for `execute_sql` and the server logs a warning on startup. :::danger Write mode lets an AI assistant run destructive statements against your database. Never enable it against production data. ::: ## Learn More For advanced usage and troubleshooting, refer to the [GreptimeDB MCP Server documentation](https://github.com/GreptimeTeam/greptimedb-mcp-server). :::note The GreptimeDB MCP Server is an experimental project still under development. Exercise caution when using it with sensitive data. ::: --- ## Metabase [Metabase](https://github.com/metabase/metabase) is an open source BI tool written in Clojure. You can configure GreptimeDB as a Metabase data source through a community driver plugin. ## Installation Download the driver plugin file `greptimedb.metabase-driver.jar` from its [release page](https://github.com/greptimeteam/greptimedb-metabase-driver/releases/latest/). Copy the jar file to `plugins/` of metabase's working directory. It will be discovered by Metabase automatically. ## Add GreptimeDB as database To add GreptimeDB database, select *Settings* / *Admin Settings* / *Databases*, click *Add Database* button and select GreptimeDB from *Database type*. You will be asked to provide host, port, database name and authentication information. - Use GreptimeDB's PostgreSQL protocol port `4003` as port. If you changed the defaults, use your own settings. - Username and password are optional if you didn't enable [authentication](/user-guide/deployments-administration/authentication/overview.md). - Use `public` as default *Database name*. When using GreptimeCloud instance, use the database name from your instance. --- ## MindsDB [MindsDB](https://mindsdb.com/) is an open-source machine learning platform that enables developers to easily incorporate advanced machine learning capabilities with existing databases. MindsDB reaches GreptimeDB through the MySQL protocol using its GreptimeDB handler. The handler ships as a [community handler](https://github.com/mindsdb/community-handlers/tree/main/community_handlers/greptimedb_handler), and community handlers are disabled by default. Enable them before creating the data source, then restart MindsDB: ```bash export MINDSDB_COMMUNITY_HANDLERS=true ``` Once enabled, configure GreptimeDB as a data source: ```sql CREATE DATABASE greptime_datasource WITH ENGINE = 'greptimedb', PARAMETERS = { "host": "", "port": 4002, "database": "", "user": "", "password": "", "ssl": True }; ``` - `` is the hostname or IP address of your GreptimeDB instance. - `` is the name of the database you want to connect to. - `` and `` are your [GreptimeDB credentials](/user-guide/deployments-administration/authentication/static.md). MindsDB is a great gateway for many machine learning features, including time-series forecasting, for your time series data stored in our instance. See [MindsDB docs](https://docs.mindsdb.com/what-is-mindsdb) for more information. --- ## Integrations GreptimeDB works with common tools for data ingestion, querying, visualization, and alerting. Most tools connect through GreptimeDB's MySQL, PostgreSQL, gRPC, Prometheus, OpenTelemetry, or InfluxDB line protocol endpoints. The pages below cover the settings for each tool. --- ## Perses [Perses](https://perses.dev/) is a CNCF observability dashboard project. You can connect a Perses instance to GreptimeDB with two official datasource plugins: - **GreptimeDB datasource** — run SQL against metrics, logs, and traces tables - **Prometheus datasource** — run PromQL against GreptimeDB's Prometheus-compatible API The [GreptimeDB plugin](https://github.com/perses/plugins/tree/main/schemas/datasources/greptimedb) is part of the CNCF Perses plugin repository. Together, these plugins let you build unified observability dashboards on top of GreptimeDB's single-database storage. See [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md) for the unified data model. GreptimeDB also embeds Perses in the [built-in Dashboard](/getting-started/installation/greptimedb-dashboard.md) (**Visualization** menu). Open `http://localhost:4000/dashboard`, click **Visualization**, and use the same datasource plugins described below. You can also configure GreptimeDB in a [standalone Perses deployment](#prerequisites). ## Supported query types | Query type | Datasource plugin | Query plugin | Panel types | | --- | --- | --- | --- | | **PromQL** | `PrometheusDatasource` | `PrometheusTimeSeriesQuery` | `TimeSeriesChart`, `GaugeChart`, `StatChart` | | **SQL time series** | `GreptimeDBDatasource` | `GreptimeDBTimeSeriesQuery` | `TimeSeriesChart`, `StatChart`, `Table` | | **Logs** | `GreptimeDBDatasource` | `GreptimeDBLogQuery` | `LogsTable` | | **Traces** | `GreptimeDBDatasource` | `GreptimeDBTraceQuery` | `TraceTable`, `TracingGanttChart` | Use **PromQL** for standard metrics dashboards. Use the **GreptimeDB** datasource for logs, traces, and SQL-based time series with `RANGE`, `ALIGN`, and `FILL`. ## Prerequisites - A running GreptimeDB instance. See [GreptimeDB Standalone](/getting-started/installation/greptimedb-standalone.md) or [GreptimeDB Cluster](/getting-started/installation/greptimedb-cluster.md). Ensure the GreptimeDB HTTP API is reachable from Perses. - A running [Perses](https://perses.dev/perses/docs/installation/) instance. Recent Perses releases ship the GreptimeDB and Prometheus datasource plugins. ## Choose a datasource | Scenario | Perses plugin | GreptimeDB endpoint | | --- | --- | --- | | Prometheus metrics, `node_exporter`, existing PromQL dashboards | `PrometheusDatasource` | `http://:4000/v1/prometheus` | | Log tables, trace tables, SQL time series (`RANGE`, `ALIGN`) | `GreptimeDBDatasource` | `http://:4000` | Use **PromQL** for standard metrics dashboards. Use the **GreptimeDB** datasource for logs, traces, and advanced SQL aggregation. ## GreptimeDB datasource plugin The GreptimeDB datasource plugin sends SQL queries to GreptimeDB over HTTP. ### Connection settings In Perses, go to **Settings → Datasources → Add**. Select **GreptimeDB Datasource** as the plugin, set **Access Type** to **Direct access**, and fill in the URL: ```txt http://:4000 ``` ![GreptimeDB datasource connection](/perses/greptimedb-datasource-connection.png) Click **Save** to create the datasource. ### Metrics query (SQL) Add a panel with type **Time Series Chart**. In the **Query** tab, select **GreptimeDB Time Series Query** and the GreptimeDB datasource. Bind panel queries to the dashboard time range with `${__from}` and `${__to}`. These variables are millisecond timestamps. Use `to_timestamp_millis()` to match GreptimeDB timestamp columns: ```sql SELECT date_bin(INTERVAL '1 minute', "ts") AS "time", "host", avg("cpu_usage") AS "value" FROM public."cpu_metrics_30" WHERE "ts" >= to_timestamp_millis(${__from}) AND "ts" <= to_timestamp_millis(${__to}) GROUP BY "time", "host" ORDER BY "time" ASC, "host" ASC; ``` Click **Run Query** to preview the chart, then **Apply** to save the panel. ![SQL time series panel](/perses/sql-timeseries-panel.png) See [SQL](/user-guide/query-data/sql.md) for query syntax. ### Logs query Add a panel with type **Logs Table**. In the **Query** tab, select **GreptimeDB Log Query** and the GreptimeDB datasource. Write a SQL query against your log table. Use `${__from}` and `${__to}` to filter by the dashboard time range: ```sql SELECT * FROM syslog WHERE greptime_timestamp >= to_timestamp_millis(${__from}) AND greptime_timestamp <= to_timestamp_millis(${__to}) ``` Click **Run Query** to preview the logs, then **Apply** to save the panel. ![Logs panel](/perses/logs-panel.png) See [Log Query](/user-guide/query-data/log-query.md) and [Logs](/user-guide/logs/overview.md). ### Traces For trace search and Gantt views, use [Traces Query](/getting-started/installation/greptimedb-dashboard.md#traces-query) in the [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) — no extra configuration required. ## Prometheus datasource plugin The Prometheus datasource plugin queries GreptimeDB through its Prometheus-compatible HTTP API. ### Connection settings In Perses, go to **Settings → Datasources → Add**. Select **Prometheus** as the plugin, set **Access Type** to **Direct access**, and fill in the URL: ```txt http://:4000/v1/prometheus ``` Click **Save** to create the datasource. ### PromQL query Use `PrometheusTimeSeriesQuery` with panels such as `TimeSeriesChart`, `GaugeChart`, or `StatChart`. ```promql node_cpu_seconds_total{mode="idle"} ``` See [PromQL](/user-guide/query-data/promql.md) for query syntax. ## Migrate Grafana dashboards GreptimeDB is compatible with the Prometheus ecosystem. You can import existing Grafana dashboards into Perses with the [Perses migration tool](https://perses.dev/perses/docs/migration/). After migration, map PromQL panels to the **Prometheus** datasource pointing at GreptimeDB. The migration is best-effort, so review the result rather than assuming it is complete: - Only dashboards are migrated. Alerts, users and other Grafana resources are not. - Upstream supports Grafana 9.0.0 through 11.x. Outside that range the migration is not guaranteed to work. - Perses does not implement every Grafana plugin. Panels and variables it cannot map are replaced with static placeholders, which you then have to rebuild by hand. ## Next steps - [Grafana integration](/user-guide/integrations/grafana.md) — connect Grafana to GreptimeDB - [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) — built-in query UI with embedded Perses - [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md) — unified observability storage --- ## Prometheus(Integrations) ## Remote Write GreptimeDB can be used as a remote storage backend for Prometheus. For detailed information, please refer to the [Ingest Data with Prometheus Remote Write](/user-guide/ingest-data/for-observability/prometheus.md) document. ## Prometheus Query Language (PromQL) GreptimeDB supports the Prometheus Query Language (PromQL) for querying metrics. For more information, please refer to the [Query Data with Prometheus Query Language](/user-guide/query-data/promql.md) document. --- ## Apache Spark The [GreptimeDB connector for Apache Spark](https://github.com/GreptimeTeam/spark-connector-greptimedb) is a DataSource V2 connector that writes batch DataFrames and Structured Streaming micro-batches to GreptimeDB. The connector requires Java 17 or later and the Scala 2.13 distribution of Apache Spark 4.2.0. It is write-only and requires an existing GreptimeDB table. ## Install the connector ### Download from Maven Central For a Maven-based Spark application, add the connector as a dependency: ```xml io.greptime spark-connector-greptimedb 0.1.0 ``` To load the connector directly into a Spark runtime, download the shaded JAR from [Maven Central](https://central.sonatype.com/artifact/io.greptime/spark-connector-greptimedb/0.1.0): ```bash mvn dependency:copy \ -Dartifact=io.greptime:spark-connector-greptimedb:0.1.0:jar:shaded \ -DoutputDirectory=/path/to/spark/jars ``` ### Build from source Build the connector from source: ```bash git clone https://github.com/GreptimeTeam/spark-connector-greptimedb.git cd spark-connector-greptimedb mvn package ``` The build creates a shaded JAR under `target/`. Add it when submitting your Spark application: ```bash ./bin/spark-submit \ --jars /path/to/spark-connector-greptimedb-*-shaded.jar \ /path/to/application.jar ``` ## Write a batch DataFrame Create the destination table in GreptimeDB before writing data: ```sql CREATE TABLE cpu_metrics ( ts TIMESTAMP(6) TIME INDEX, host STRING, usage DOUBLE, PRIMARY KEY (host) ); ``` Then write a DataFrame in append mode: ```java Dataset metrics = ...; metrics.write() .format("greptimedb") .mode("append") .option("endpoints", "127.0.0.1:4001") .option("database", "public") .option("table", "cpu_metrics") .option("time-index", "ts") .option("tags", "host") .option("batch.max-rows", "1000") .save(); ``` `endpoints` uses the GreptimeDB gRPC endpoint, whose default port is `4001`. The `time-index` column must have the Spark SQL type `TIMESTAMP` or `TIMESTAMP_NTZ`, and its values must not be null. Columns listed in `tags` should match the primary key columns of the destination GreptimeDB table. ## Write a Structured Streaming DataFrame Use the connector as a Structured Streaming sink in append output mode: ```java StreamingQuery query = metrics.writeStream() .format("greptimedb") .outputMode("append") .option("checkpointLocation", "/path/to/checkpoint") .option("endpoints", "127.0.0.1:4001") .option("database", "public") .option("table", "cpu_metrics") .option("time-index", "ts") .option("tags", "host") .start(); ``` ## Connector options | Option | Required | Default | Description | | --- | --- | --- | --- | | `endpoints` | Yes | - | Comma-separated GreptimeDB gRPC endpoints in `hostname:port` or `IPv4:port` format. | | `table` | Yes | - | Existing destination table. | | `time-index` | Yes | - | `TIMESTAMP` or `TIMESTAMP_NTZ` column used as the GreptimeDB time index. | | `database` | No | `public` | Destination database. | | `tags` | No | - | Comma-separated columns written as GreptimeDB tags. | | `username` | No | - | GreptimeDB username. Configure with `password`. | | `password` | No | - | GreptimeDB password. Configure with `username`. | | `batch.max-rows` | No | `1000` | Rows per ingester bulk message in each Spark task. | For advanced bulk-write options, see the [connector README](https://github.com/GreptimeTeam/spark-connector-greptimedb#options). ## Supported data types | Spark SQL type | GreptimeDB type | | --- | --- | | `BOOLEAN` | `BOOLEAN` | | `TINYINT` | `INT8` | | `SMALLINT` | `INT16` | | `INT` | `INT32` | | `BIGINT` | `INT64` | | `FLOAT` | `FLOAT32` | | `DOUBLE` | `FLOAT64` | | `STRING`, `CHAR`, `VARCHAR` | `STRING` | | `BINARY` | `BINARY` | | `DATE` | `DATE` | | `TIMESTAMP`, `TIMESTAMP_NTZ` | `TIMESTAMP_MICROSECOND` | | `DECIMAL(p, s)` | `DECIMAL128(p, s)` | The connector rejects unsupported complex Spark SQL types before submitting the job. It does not validate the server-side schema, so the DataFrame columns and mapped types must match the destination table. Writes are insert-only and provide at-least-once delivery. Failed or retried Spark tasks and streaming epochs can produce duplicate rows. The connector does not support reads, overwrite, delete, upsert, automatic table creation, or exactly-once commits. --- ## Streamlit [Streamlit](https://streamlit.io/) is an open-source Python framework for building data apps. You can back a Streamlit app with data stored in GreptimeDB. To use GreptimeDB data in your application, you will need to create a SQL connection. Thanks to GreptimeDB's [MySQL protocol compatibility](/user-guide/protocols/mysql.md), you can treat GreptimeDB as MySQL when connecting to it. Here is an example code snippet to connect to GreptimeDB from Streamlit: ```python st.title('GreptimeDB Streamlit Demo') conn = st.connection("greptimedb", type="sql", url="mysql://:@:4002/") df = conn.query("SELECT * FROM ...") ``` - The `` is the hostname or IP address of your GreptimeDB instance. - The `` is the name of the database you want to connect to. - The `` and `` are your [GreptimeDB credentials](/user-guide/deployments-administration/authentication/static.md). Once you have created the connection, you can run SQL query against your GreptimeDB instance. The resultset is automatically converted to Pandas dataframe just like normal data source in streamlit. --- ## Superset [Apache Superset](https://superset.apache.org) is an open source BI tool that written in Python. To configure GreptimeDB as a database in Superset, you can follow this guide. ## Installation ### Running Superset with Docker Compose [Docker compose](https://superset.apache.org/docs/installation/docker-compose) is the quickest way to try Superset locally. Upstream states it does not support or recommend the `docker compose` constructs for production use, so treat this path as evaluation only. To add GreptimeDB extension, create a `requirements-local.txt` file in `docker/` of Superset codebase. Add GreptimeDB dependency in `requirements-local.txt`: ```txt greptimedb-sqlalchemy ``` Start Superset services: ```bash docker compose -f docker-compose-non-dev.yml up ``` ### Running Superset Locally If you are [running Superset from pypi](https://superset.apache.org/docs/installation/pypi), install our extension to the same environment. ```bash pip install greptimedb-sqlalchemy ``` :::note SQLAlchemy version `greptimedb-sqlalchemy` requires `sqlalchemy>=1.4,<2`. Superset 6.1.0, the current release, pins SQLAlchemy 1.4.54 and works with it. Superset's main branch has moved to SQLAlchemy 2.0, so a later release will need a dialect that supports 2.0 first. If you run an older Superset, check its own SQLAlchemy pin against that range. ::: ## Add GreptimeDB as database To add GreptimeDB database, select *Settings* / *Database Connections*. Add database and select *GreptimeDB* from list of supported databases. Follow the SQLAlchemy URI pattern to provide your connection information: ``` greptimedb://:@:/ ``` - Ignore `:@` if you don't have [authentication](/user-guide/deployments-administration/authentication/overview.md) enabled. - Use `4003` for default port (this extension uses Postgres protocol). - Use `public` as default `database`. When using GreptimeCloud instance, use the database name from your instance. --- ## Telegraf For instructions on how to write data to GreptimeDB using Telegraf, please refer to the [Telegraf section](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md#telegraf) in the InfluxDB Line Protocol documentation. --- ## Vector(Integrations) Please refer to the [Ingest Data with Vector](/user-guide/ingest-data/for-observability/vector.md) document for instructions on how to sink data to GreptimeDB using Vector. --- ## vmalert [vmalert](https://docs.victoriametrics.com/vmalert/) supports defining alerting and recording rules in Prometheus-style YAML configuration. It can query GreptimeDB through the Prometheus HTTP API to evaluate rule expressions. When integrating vmalert with GreptimeDB, version `1.148.4` or later is recommended. ## Define rules vmalert uses a YAML rule configuration format similar to that used by Prometheus. See the [vmalert rule documentation][vmalert-rules] for supported configuration options. [vmalert-rules]: https://docs.victoriametrics.com/victoriametrics/vmalert/#rules The following example defines an alerting rule and a recording rule that query GreptimeDB using PromQL. It assumes that the database already contains an `up` metric and an `http_requests_total` counter, both with a `job` label. The alerting rule monitors targets with `job="api"`. Adjust this value to match your `job` label. Save the following configuration as `/etc/vmalert/greptimedb-rules.yaml`: ```yaml groups: - name: greptimedb interval: 1m rules: - alert: InstanceDown expr: up{job="api"} == 0 for: 5m labels: severity: warning annotations: summary: "API instance is down" - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) ``` The rule group is evaluated every minute. The two rules work as follows: - `InstanceDown`: Enters the firing state when a time series with `job="api"` continuously satisfies `up == 0` for 5 minutes. - `job:http_requests:rate5m`: Calculates the average HTTP request rate over the past 5 minutes, summed by `job`, and writes the results to GreptimeDB through the remote write endpoint. The recorded metric can be used in traffic dashboards and alerts without repeating the rate calculation. ## Configure GreptimeDB as the data source After creating the rule file, start vmalert with the following command. It configures GreptimeDB as the data source for querying metrics and the remote write endpoint for storing recording rule results: ```shell GREPTIME_URL="http://:4000" vmalert \ -rule=/etc/vmalert/greptimedb-rules.yaml \ -datasource.url="${GREPTIME_URL}/v1/prometheus?db=public" \ -datasource.headers='Content-Type: application/x-www-form-urlencoded' \ -remoteWrite.url="${GREPTIME_URL}/v1/prometheus/write?db=public" \ -remoteWrite.disablePathAppend \ -notifier.blackhole ``` Replace `` with your GreptimeDB host and `public` with the database that stores your metrics. The options are: - `-rule`: Specifies the rule file created in the previous step. - `-datasource.url`: Sets the root path of GreptimeDB's Prometheus query API. The `db` parameter selects the database. - `-datasource.headers`: Sets `Content-Type` to `application/x-www-form-urlencoded`, as required by GreptimeDB for query requests from vmalert. - `-remoteWrite.url`: Sets GreptimeDB's remote write endpoint for storing recording rule results. - `-remoteWrite.disablePathAppend`: Prevents vmalert from appending `/api/v1/write` to the URL, so it uses the specified `/v1/prometheus/write` path. - `-notifier.blackhole`: Disables alert notifications while allowing alerting rules to run. If authentication is enabled, configure vmalert to authenticate its requests to both the data source and remote write endpoint. See [HTTP authentication](/user-guide/protocols/http.md#authentication) for GreptimeDB authentication methods. The example uses `-notifier.blackhole`, so it does not send notifications. To send notifications, remove this flag and configure a notifier such as Alertmanager according to the [vmalert documentation](https://docs.victoriametrics.com/vmalert/). ## Verify recording rule results After vmalert writes the recording rule results to GreptimeDB, query them through the Prometheus HTTP API: ```shell curl --get "${GREPTIME_URL}/v1/prometheus/api/v1/query" \ --data-urlencode 'db=public' \ --data-urlencode 'query=job:http_requests:rate5m' ``` If `http_requests_total` contains enough samples in the 5-minute window for the rule to evaluate, the query returns the recorded request rate for each `job`, measured in requests per second. --- ## Full-Text Search This document provides a guide on how to use GreptimeDB's query language for effective searching and analysis of log data. GreptimeDB allows for flexible querying of data using SQL statements. This section introduces specific search functions and query statements designed to enhance your log querying capabilities. ## Pattern Matching Using the `matches_term` Function In SQL statements, you can use the `matches_term` function to perform script-aware term/phrase matching, which is especially useful for log analysis. The `matches_term` function supports pattern matching on `String` type columns. You can also use the `@@` operator as a shorthand for `matches_term`. Here's an example of how it can be used: ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'error') OR matches_term(message, 'fail'); -- Using @@ operator (shorthand for matches_term) SELECT * FROM logs WHERE message @@ 'error' OR message @@ 'fail'; ``` The `matches_term` function finds an exact character sequence and applies boundary rules based on the characters in the search term: - `text`: The text column to search, which should contain textual data of type `String`. - `term`: The search term or phrase, following these rules: - Case-sensitive matching - For terms without Han characters, word-boundary rules apply. ASCII alphanumeric edges cannot be adjacent to ASCII alphanumeric characters; other Unicode alphanumeric edges cannot be adjacent to alphanumeric or Han characters. - Terms containing Han characters match as contiguous substrings without requiring word boundaries. - Supports whole-word matching and phrase matching ## Query Statements ### Simple Term Matching For terms without Han characters, `matches_term` performs exact word matching. The match must have the boundaries described above or occur at the start or end of the text. This is particularly useful for finding specific error messages, status codes, version numbers, or paths in logs. Error messages: ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'error'); -- Using @@ operator SELECT * FROM logs WHERE message @@ 'error'; ``` Examples of matches and non-matches: - ✅ "An error occurred!" - matches because "error" is a complete word - ✅ "Critical error: system failure" - matches because "error" is bounded by space and colon - ✅ "error-prone" - matches because "error" is bounded by hyphen - ❌ "errors" - no match because "error" is part of a larger word - ❌ "error123" - no match because "error" is followed by numbers - ❌ "errorLogs" - no match because "error" is part of a camelCase word File paths and commands: ```sql -- Find specific command with path SELECT * FROM logs WHERE matches_term(message, '/start'); ``` Examples of matches and non-matches for '/start': - ✅ "GET /app/start" - matches because "/start" is a complete term - ✅ "Command: /start-process" - matches because "/start" is bounded by hyphen - ✅ "Command: /start" - matches because "/start" is at the end of the message - ❌ "start" - no match because it's missing the leading slash - ❌ "start/stop" - no match because "/start" is not a complete term ### Han Term Matching Terms containing Han characters use contiguous substring matching and do not require word boundaries. ```sql SELECT * FROM logs WHERE matches_term(message, '手机'); ``` Examples of matches and non-matches: - ✅ "登录手机号18888888888的动态key" - matches because it contains the exact substring "手机" - ❌ "手持设备" - no match because it does not contain the exact substring "手机" ### Multiple Term Searches You can combine multiple `matches_term` conditions using the `OR` operator to search for logs containing any of several terms. This is useful when you want to find logs that might contain different variations of an error or different types of issues. ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'critical') OR matches_term(message, 'error'); -- Using @@ operator SELECT * FROM logs WHERE message @@ 'critical' OR message @@ 'error'; ``` This query will find logs containing either "critical" or "error" as complete words. Each term is matched independently, and the results include logs that match either condition. Examples of matches and non-matches: - ✅ "critical error: system failure" - matches both terms - ✅ "An error occurred!" - matches "error" - ✅ "critical failure detected" - matches "critical" - ❌ "errors" - no match because "error" is part of a larger word - ✅ "critical_errors" - matches "critical" because an underscore is a non-alphanumeric boundary ### Exclusion Searches You can use the `NOT` operator with `matches_term` to exclude certain terms from your search results. This is useful when you want to find logs containing one term but not another. ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'error') AND NOT matches_term(message, 'critical'); -- Using @@ operator SELECT * FROM logs WHERE message @@ 'error' AND NOT message @@ 'critical'; ``` This query will find logs containing the word "error" but not containing the word "critical". This is particularly useful for filtering out certain types of errors or focusing on specific error categories. Examples of matches and non-matches: - ✅ "An error occurred!" - matches because it contains "error" but not "critical" - ❌ "critical error: system failure" - no match because it contains both terms - ❌ "critical failure detected" - no match because it contains "critical" ### Required Term Searches You can use the `AND` operator to require that multiple terms be present in the log message. This is useful for finding logs that contain specific combinations of terms. ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'critical') AND matches_term(message, 'error'); -- Using @@ operator SELECT * FROM logs WHERE message @@ 'critical' AND message @@ 'error'; ``` This query will find logs containing both "critical" and "error" as complete words. Both conditions must be satisfied for a log to be included in the results. Examples of matches and non-matches: - ✅ "critical error: system failure" - matches because it contains both terms - ❌ "An error occurred!" - no match because it only contains "error" - ❌ "critical failure detected" - no match because it only contains "critical" ### Phrase Matching The `matches_term` function can also match exact phrases, including those with spaces. This is useful for finding specific error messages or status updates that contain multiple words. ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(message, 'system failure'); -- Using @@ operator SELECT * FROM logs WHERE message @@ 'system failure'; ``` This query will find logs containing the exact phrase "system failure" with proper boundaries. The entire phrase must match exactly, including the space between words. Examples of matches and non-matches: - ✅ "Alert: system failure detected" - matches because the phrase is properly bounded - ✅ "system failure!" - matches because the phrase is properly bounded - ❌ "system-failure" - no match because the words are separated by a hyphen instead of a space - ❌ "system failure2023" - no match because the phrase is followed by numbers ### Case-Insensitive Matching While `matches_term` is case-sensitive by default, you can achieve case-insensitive matching by converting the text to lowercase before matching. ```sql -- Using matches_term function SELECT * FROM logs WHERE matches_term(lower(message), 'warning'); -- Using @@ operator SELECT * FROM logs WHERE lower(message) @@ 'warning'; ``` This query will find logs containing the word "warning" regardless of its case. The `lower()` function converts the entire message to lowercase before matching. Examples of matches and non-matches: - ✅ "Warning: high temperature" - matches after case conversion - ✅ "WARNING: system overload" - matches after case conversion - ❌ "warned" - no match because it's a different word - ❌ "warnings" - no match because it's a different word --- ## JSON2 Type JSON2 is a JSON type in GreptimeDB designed for logs and semi-structured data. It stores fields inside JSON in a structured, columnar form so that frequently used fields can be read, filtered, and aggregated efficiently like regular columns, while still preserving the flexibility of JSON for dynamic schemas. :::note JSON2 is currently in Beta, and some capabilities are still being improved. ::: ## Quick Start The following example creates an API access log table, inserts a few request logs, and queries fields from JSON2. Fixed fields are stored in regular columns, while fields in `attrs` use JSON2 because their structure may vary but they are still queried frequently. ### Create a table When creating a table, you can declare a JSON2 column with the `JSON2` type. Currently, JSON2 can only be used in append-only tables, so you must set `'append_mode' = 'true'` when creating the table. ```sql CREATE TABLE application_logs ( ts TIMESTAMP TIME INDEX, app_name STRING, log_level STRING, `message` STRING, attrs JSON2, ) WITH ( 'append_mode' = 'true' ); ``` ### Insert JSON data When writing to a JSON2 column, you can insert a JSON object. The following data includes one successful request, one slow request, and one failed request: ```sql INSERT INTO application_logs VALUES ( 1, 'checkout', 'INFO', 'request completed', '{"trace_id":"8f3a1c","user":{"id":1001,"name":"Alice"},"http":{"method":"POST","path":"/v1/orders","status":200},"latency_ms":42.8}' ), ( 2, 'checkout', 'WARN', 'slow request', '{"trace_id":"8f3a1d","user":{"id":1002,"name":"Bob"},"http":{"method":"POST","path":"/v1/orders","status":200},"latency_ms":386.4}' ), ( 3, 'checkout', 'ERROR', 'request failed', '{"trace_id":"8f3a1e","user":{"id":1003},"http":{"method":"POST","path":"/v1/orders","status":500},"latency_ms":71.2,"error":true}' ); ``` Custom pipelines can also write JSON2 columns by using `type: json2` in the transform configuration. See the [pipeline configuration reference](/reference/pipeline/pipeline-config.md#the-type-field) for the supported type-hint options. ### Query JSON fields You can read fields from JSON2 directly with dot paths: ```sql SELECT ts, app_name, attrs.trace_id AS trace_id, attrs.user.name AS user_name, attrs.http.status AS status, attrs.latency_ms AS latency_ms, attrs.error AS error FROM application_logs ORDER BY ts; ``` The query result is: | ts | app_name | trace_id | user_name | status | latency_ms | error | | --- | --- | --- | --- | --- | --- | --- | | 1970-01-01 00:00:00.001 | checkout | 8f3a1c | Alice | 200 | 42.8 | NULL | | 1970-01-01 00:00:00.002 | checkout | 8f3a1d | Bob | 200 | 386.4 | NULL | | 1970-01-01 00:00:00.003 | checkout | 8f3a1e | NULL | 500 | 71.2 | true | You can also select the complete JSON2 value: ```sql SELECT ts, attrs FROM application_logs ORDER BY ts; ``` You can also use JSON functions and cast the return type explicitly: ```sql SELECT json_get(attrs, 'http.path')::STRING AS path, json_get(attrs, 'http.status')::INT8 AS status, json_get(attrs, 'latency_ms')::DOUBLE AS latency_ms, json_get(attrs, 'error')::BOOLEAN AS error FROM application_logs WHERE json_get(attrs, 'http.status')::INT8 >= 500 OR json_get(attrs, 'latency_ms')::DOUBLE > 300 ORDER BY ts; ``` The query result is: | path | status | latency_ms | error | | --- | --- | --- | --- | | /v1/orders | 200 | 386.4 | NULL | | /v1/orders | 500 | 71.2 | true | You can also aggregate fields, for example to count requests, errors, and average latency for each API path: ```sql SELECT json_get(attrs, 'http.path')::STRING AS path, COUNT(*) AS requests, SUM(CASE WHEN json_get(attrs, 'error')::BOOLEAN THEN 1 ELSE 0 END) AS errors, ROUND(AVG(json_get(attrs, 'latency_ms')::DOUBLE), 1) AS avg_latency_ms FROM application_logs GROUP BY json_get(attrs, 'http.path')::STRING; ``` The query result is: | path | requests | errors | avg_latency_ms | | --- | --- | --- | --- | | /v1/orders | 3 | 1 | 166.8 | ## Syntax ### JSON Field Type hints JSON2 supports type hints for declaring concrete data types for selected subpaths. Type hints are recommended for frequently queried subpaths with known and stable types. These subpaths are stored using the specified types, providing query performance close to regular columns. JSON2 also validates their values during writes. Type hints are optional. For subpaths without type hints, JSON2 infers their types from the values written to the column. The syntax for declaring type hints is: ```sql json_column JSON2 ( path.to.field DATA_TYPE [NULL | NOT NULL] [DEFAULT literal] ) ``` Type hint paths use dot notation. For example, `user.id` refers to the following JSON path: `{"user":{"id":...}}`. If a JSON key itself contains a dot, wrap that path segment in double quotes. For example, `"service.name"` means a key named `service.name` in the root object, not a nested path `service.name`. Type hints currently support the following data types: - `STRING` - `BIGINT` - `BIGINT UNSIGNED` - `DOUBLE` - `BOOLEAN` Type hints allow `NULL` by default. If you specify `NOT NULL`, that path must exist in the written JSON. You can declare type hints directly in the `CREATE TABLE` statement. The following example defines type hints for commonly queried subpaths in the `attrs` column: ```sql CREATE TABLE application_logs ( ts TIMESTAMP TIME INDEX, app_name STRING, log_level STRING, `message` STRING, attrs JSON2 ( trace_id STRING, user.id BIGINT, user.name STRING DEFAULT 'anonymous', http.method STRING, http.path STRING, http.status BIGINT, latency_ms DOUBLE, error BOOLEAN DEFAULT false ) ) WITH ( 'append_mode' = 'true' ); ``` ### `json_get` UDF `json_get` reads a nested field from JSON2 by path. It returns a string by default. If you want to specify the return type directly, add a cast after the function. The syntax of `json_get` is: ```sql json_get(json_column, 'path.to.field')::TYPE ``` `json_get` can be used in `SELECT`, `WHERE`, `GROUP BY`, and other SQL clauses that accept expressions. For example: ```sql SELECT json_get(attrs, 'trace_id')::STRING AS trace_id, json_get(attrs, 'http.status')::BIGINT AS status, json_get(attrs, 'latency_ms')::DOUBLE AS latency_ms FROM application_logs WHERE json_get(attrs, 'http.status')::BIGINT >= 500; ``` The typed extraction functions `json_get_string`, `json_get_int`, `json_get_float`, and `json_get_bool` also accept JSON2 values. See [JSON functions](/reference/sql/functions/json.md#extraction) for details. ### Dot syntax You can read JSON2 subpaths directly with dot syntax: ```sql json_column.path.to.field ``` Dot syntax can be used in `SELECT`, `WHERE`, `GROUP BY`, and other SQL clauses that accept expressions. For example: ```sql SELECT attrs.trace_id, attrs.http.status, attrs.latency_ms FROM application_logs WHERE attrs.http.status >= 500; ``` ### Control automatic path expansion By default, JSON2 expands compatible unhinted leaf paths into structured columns. Use `max_auto_expanded_paths` to limit how many paths are expanded: ```sql CREATE TABLE application_logs ( ts TIMESTAMP TIME INDEX, attrs JSON2 ( max_auto_expanded_paths = 20, trace_id STRING ) ) WITH ( 'append_mode' = 'true' ); ``` Set the option to `0` to disable automatic expansion. Type-hinted paths do not count against this limit. Fields beyond the limit remain in a special `remainder` field and can still be queried, so the option controls storage layout and performance, not the logical JSON schema. ## Current limitations JSON2 is currently in Beta and has the following limitations: - JSON2 columns can only be used in append-only tables. - Each non-NULL JSON root must be a non-empty object. Root arrays, strings, numbers, booleans, the JSON literal `null`, and empty objects are not supported. - Array elements cannot be accessed with subscript syntax. - Type hints support only the types listed above and cannot traverse arrays. - Structured expansion and type hint paths support at most 50 nested path segments. Deeper unhinted values remain queryable in the special `remainder` field. --- ## Manage Pipelines In GreptimeDB, each `pipeline` is a collection of data processing units used for parsing and transforming the ingested log content. This document provides guidance on creating and deleting pipelines to efficiently manage the processing flow of log data. For specific pipeline configurations, please refer to the [Pipeline Configuration](/reference/pipeline/pipeline-config.md) documentation. ## Authentication The HTTP API for managing pipelines requires authentication. For more information, see the [Authentication](/user-guide/protocols/http.md#authentication) documentation. ## Upload a Pipeline GreptimeDB provides a dedicated HTTP interface for creating pipelines. Assuming you have prepared a pipeline configuration file `pipeline.yaml`, use the following command to upload the configuration file, where `test` is the name you specify for the pipeline: ```shell ## Upload the pipeline file. 'test' is the name of the pipeline curl -X "POST" "http://localhost:4000/v1/pipelines/test" \ -H "Authorization: Basic " \ -F "file=@pipeline.yaml" ``` The created Pipeline is shared for all databases. ## Pipeline Versions You can upload multiple versions of a pipeline with the same name. Each time you upload a pipeline with an existing name, a new version is created automatically. You can specify which version to use when [ingesting logs](/reference/pipeline/write-log-api.md#http-api), [querying](#query-pipelines), or [deleting](#delete-a-pipeline) a pipeline. The last uploaded version is used by default if no version is specified. After successfully uploading a pipeline, the response will include version information: ```json {"name":"test","version":"2024-06-27 12:02:34.257312110Z"} ``` The version is a timestamp in UTC format that indicates when the pipeline was created. This timestamp serves as a unique identifier for each pipeline version. ## Delete a Pipeline You can use the following HTTP interface to delete a pipeline: ```shell ## 'test' is the name of the pipeline curl -X "DELETE" "http://localhost:4000/v1/pipelines/test?version=2024-06-27%2012%3A02%3A34.257312110Z" \ -H "Authorization: Basic " ``` In the above example, we deleted a pipeline named `test`. The `version` parameter is required to specify the version of the pipeline to be deleted. ## Query Pipelines Querying a pipeline with a name through HTTP interface as follow: ```shell ## 'test' is the name of the pipeline, it will return a pipeline with latest version if the pipeline named `test` exists. curl "http://localhost:4000/v1/pipelines/test" \ -H "Authorization: Basic " ``` ```shell ## with the version parameter, it will return the specify version pipeline. curl "http://localhost:4000/v1/pipelines/test?version=2025-04-01%2006%3A58%3A31.335251882%2B0000" \ -H "Authorization: Basic " ``` If the pipeline exists, the output should be: ```json { "pipelines": [ { "name": "test", "version": "2025-04-01 06:58:31.335251882", "pipeline": "version: 2\nprocessors:\n - dissect:\n fields:\n - message\n patterns:\n - '%{ip_address} - - [%{timestamp}] \"%{http_method} %{request_line}\" %{status_code} %{response_size} \"-\" \"%{user_agent}\"'\n ignore_missing: true\n - date:\n fields:\n - timestamp\n formats:\n - \"%d/%b/%Y:%H:%M:%S %z\"\n - select:\n type: exclude\n fields:\n - message\n\ntransform:\n - fields:\n - ip_address\n type: string\n index: inverted\n tag: true\n - fields:\n - status_code\n type: int32\n index: inverted\n tag: true\n - fields:\n - request_line\n - user_agent\n type: string\n index: fulltext\n - fields:\n - response_size\n type: int32\n - fields:\n - timestamp\n type: time\n index: timestamp\n" } ], "execution_time_ms": 7 } ``` In the output above, the `pipeline` field is a YAML-formatted string. You can use [`jq -r`](https://jqlang.org/) to print it in a more human-readable way: ```shell curl "http://localhost:4000/v1/pipelines/test?version=2025-04-01%2006%3A58%3A31.335251882%2B0000" \ -H "Authorization: Basic " \ | jq -r '.pipelines[0].pipeline' ``` ```yml version: 2 processors: - dissect: fields: - message patterns: - '%{ip_address} - - [%{timestamp}] "%{http_method} %{request_line}" %{status_code} %{response_size} "-" "%{user_agent}"' ignore_missing: true - date: fields: - timestamp formats: - "%d/%b/%Y:%H:%M:%S %z" - select: type: exclude fields: - message transform: - fields: - ip_address type: string index: inverted tag: true - fields: - status_code type: int32 index: inverted tag: true - fields: - request_line - user_agent type: string index: fulltext - fields: - response_size type: int32 - fields: - timestamp type: time index: timestamp ``` Or you can use SQL to query pipeline information. ```sql SELECT * FROM greptime_private.pipelines; ``` Please note that if you are using the MySQL or PostgreSQL protocol to connect to GreptimeDB, the precision of the pipeline time information may vary, and nanosecond-level precision may be lost. To address this issue, you can cast the `created_at` field to a timestamp to view the pipeline's creation time. For example, the following query displays `created_at` in `bigint` format: ```sql SELECT name, pipeline, created_at::bigint FROM greptime_private.pipelines; ``` The query result is as follows: ``` name | pipeline | greptime_private.pipelines.created_at ------+-----------------------------------+--------------------------------------- test | processors: +| 1719489754257312110 | - date: +| | field: time +| | formats: +| | - "%Y-%m-%d %H:%M:%S%.3f"+| | ignore_missing: true +| | +| | transform: +| | - fields: +| | - id1 +| | - id2 +| | type: int32 +| | - fields: +| | - type +| | - logger +| | type: string +| | index: inverted +| | - fields: +| | - log +| | type: string +| | index: fulltext +| | - field: time +| | type: time +| | index: timestamp +| | | (1 row) ``` Then, you can use a program to convert the bigint type timestamp from the SQL result into a time string. ```shell python3 -c ' ns = int(sys.argv[1]) dt = datetime.datetime.fromtimestamp(ns // 10**9, datetime.timezone.utc) print("Readable timestamp (UTC):", dt.strftime("%Y-%m-%d %H:%M:%S") + ".%09dZ" % (ns % 10**9)) ' 1719489754257312110 ``` Output: ```shell Readable timestamp (UTC): 2024-06-27 12:02:34.257312110Z ``` The output `Readable timestamp (UTC)` represents the creation time of the pipeline and also serves as the version number. ## Debug First, please refer to the [Quick Start example](/user-guide/logs/quick-start.md#direct-http-ingestion) to see the correct execution of the Pipeline. ### Debug creating a Pipeline You may encounter errors when creating a Pipeline. For example, when creating a Pipeline using the following configuration: ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/test" \ -H "Content-Type: application/x-yaml" \ -H "Authorization: Basic " \ -d $'processors: - date: field: time formats: - "%Y-%m-%d %H:%M:%S%.3f" ignore_missing: true - gsub: fields: - message pattern: "\\\." replacement: - "-" ignore_missing: true transform: - fields: - message type: string - field: time type: time index: timestamp ' ``` The pipeline configuration contains an error. The `gsub` Processor expects the `replacement` field to be a string, but the current configuration provides an array. As a result, the pipeline creation fails with the following error message: ```json {"error":"Failed to parse pipeline: 'replacement' must be a string"} ``` Therefore, We need to modify the configuration of the `gsub` Processor and change the value of the `replacement` field to a string type. ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/test" \ -H "Content-Type: application/x-yaml" \ -H "Authorization: Basic " \ -d $'processors: - date: field: time formats: - "%Y-%m-%d %H:%M:%S%.3f" ignore_missing: true - gsub: fields: - message pattern: "\\\." replacement: "-" ignore_missing: true transform: - fields: - message type: string - field: time type: time index: timestamp ' ``` Now that the Pipeline has been created successfully, you can test the Pipeline using the `dryrun` interface. ### Debug writing logs We can test the Pipeline using the `dryrun` interface. We will test it with erroneous log data where the value of the message field is in numeric format, causing the pipeline to fail during processing. **This API is only used to test the results of the Pipeline and does not write logs to GreptimeDB.** ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/_dryrun?pipeline_name=test" \ -H "Content-Type: application/json" \ -H "Authorization: Basic " \ -d $'{"message": 1998.08,"time":"2024-05-25 20:16:37.217"}' ``` Output: ```json {"error":"Processor gsub: expect string value, but got Float(1998.08)"} ``` The output indicates that the pipeline processing failed because the `gsub` Processor expects a string type rather than a floating-point number type. We need to adjust the format of the log data to ensure the pipeline can process it correctly. Let's change the value of the message field to a string type and test the pipeline again. ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/_dryrun?pipeline_name=test" \ -H "Content-Type: application/json" \ -H "Authorization: Basic " \ -d $'{"message": "1998.08","time":"2024-05-25 20:16:37.217"}' ``` At this point, the Pipeline processing is successful, and the output is as follows: ```json [ { "rows": [ [ { "data_type": "STRING", "key": "message", "semantic_type": "FIELD", "value": "1998-08" }, { "data_type": "TIMESTAMP_NANOSECOND", "key": "time", "semantic_type": "TIMESTAMP", "value": 1716668197217000000 } ] ], "schema": [ { "column_type": "FIELD", "data_type": "STRING", "fulltext": false, "name": "message" }, { "column_type": "TIMESTAMP", "data_type": "TIMESTAMP_NANOSECOND", "fulltext": false, "name": "time" } ], "table_name": "dry_run" } ] ``` It can be seen that the `.` in the string `1998.08` has been replaced with `-`, indicating a successful processing of the Pipeline. ## Get Table DDL from a Pipeline Configuration When using pipelines, GreptimeDB automatically creates target tables upon first data ingestion by default. However, you may want to manually create tables beforehand to add custom table options, such as partition rules for better performance. While the auto-created table schema is deterministic for a given pipeline configuration, manually writing the table DDL (Data Definition Language) according to the configuration can be tedious. The `/ddl` API endpoint simplifies this process. For an existing pipeline, you can use the `/v1/pipelines/{pipeline_name}/ddl` endpoint to generate the `CREATE TABLE` SQL. This API examines the transform definition in the pipeline configuration and infers the appropriate table schema. You can use this API to generate the basic table DDL, fine-tune table options and manually create the table before ingesting data. Some common cases would be: - Add [partition rules](/user-guide/deployments-administration/manage-data/table-sharding.md) - Modify [index options](/user-guide/manage-data/data-index.md) - Add other [table options](/reference/sql/create.md#table-options) Here is an example demonstrating how to use this API. Consider the following pipeline configuration: ```YAML # pipeline.yaml processors: - dissect: fields: - message patterns: - '%{ip_address} - %{username} [%{timestamp}] "%{http_method} %{request_line} %{protocol}" %{status_code} %{response_size}' ignore_missing: true - date: fields: - timestamp formats: - "%d/%b/%Y:%H:%M:%S %z" transform: - fields: - timestamp type: time index: timestamp - fields: - ip_address type: string index: skipping - fields: - username type: string tag: true - fields: - http_method type: string index: inverted - fields: - request_line type: string index: fulltext - fields: - protocol type: string - fields: - status_code type: int32 index: inverted tag: true - fields: - response_size type: int64 on_failure: default default: 0 - fields: - message type: string ``` First, upload the pipeline to the database using the following command: ```bash curl -X "POST" "http://localhost:4000/v1/pipelines/pp" -F "file=@pipeline.yaml" ``` Then, query the table DDL using the following command: ```bash curl -X "GET" "http://localhost:4000/v1/pipelines/pp/ddl?table=test_table" ``` The API returns the following output in JSON format: ```JSON { "sql": { "sql": "CREATE TABLE IF NOT EXISTS `test_table` (\n `timestamp` TIMESTAMP(9) NOT NULL,\n `ip_address` STRING NULL SKIPPING INDEX WITH(false_positive_rate = '0.01', granularity = '10240', type = 'BLOOM'),\n `username` STRING NULL,\n `http_method` STRING NULL INVERTED INDEX,\n `request_line` STRING NULL FULLTEXT INDEX WITH(analyzer = 'English', backend = 'bloom', case_sensitive = 'false', false_positive_rate = '0.01', granularity = '10240'),\n `protocol` STRING NULL,\n `status_code` INT NULL INVERTED INDEX,\n `response_size` BIGINT NULL,\n `message` STRING NULL,\n TIME INDEX (`timestamp`),\n PRIMARY KEY (`username`, `status_code`)\n)\nENGINE=mito\nWITH(\n append_mode = 'true'\n)" }, "execution_time_ms": 3 } ``` After formatting the `sql` field in the response, you can see the inferred table schema: ```SQL CREATE TABLE IF NOT EXISTS `test_table` ( `timestamp` TIMESTAMP(9) NOT NULL, `ip_address` STRING NULL SKIPPING INDEX WITH(false_positive_rate = '0.01', granularity = '10240', type = 'BLOOM'), `username` STRING NULL, `http_method` STRING NULL INVERTED INDEX, `request_line` STRING NULL FULLTEXT INDEX WITH(analyzer = 'English', backend = 'bloom', case_sensitive = 'false', false_positive_rate = '0.01', granularity = '10240'), `protocol` STRING NULL, `status_code` INT NULL INVERTED INDEX, `response_size` BIGINT NULL, `message` STRING NULL, TIME INDEX (`timestamp`), PRIMARY KEY (`username`, `status_code`) ) ENGINE=mito WITH( append_mode = 'true' ) ``` You can use the inferred table DDL as a starting point. After customizing the DDL to meet your requirements, execute it manually before ingesting data through the pipeline. **Notes:** 1. The API only infers the table schema from the pipeline configuration; it doesn't check if the table already exists. 2. The API doesn't account for table suffixes. If you're using `dispatcher`, `table_suffix`, or table suffix hints in your pipeline configuration, you'll need to adjust the table name manually. --- ## Logs Logs are stored in the same engine as metrics and traces, so a log table can be queried with SQL and joined against the other signals. What is specific to logs is the stage before storage: a pipeline parses a raw line into columns and decides which of them get indexed. ![log-collection-flow](/log-collection-flow.drawio.svg) A log collector sends raw lines to GreptimeDB, a pipeline turns them into rows, and the result is a table you can search with SQL and full-text indexes. ## How a log becomes a row A metric arrives already structured: a name, a set of labels, a value. An unstructured log line does not: it is one string, and what is inside it depends on whoever wrote the logging statement. The pipeline is the step that closes that gap, and it runs on the write path, before anything is stored. A pipeline has two stages: - **Processors** parse and rewrite the incoming fields. `dissect` and `regex` split a raw line into named fields, `json_parse` and `csv` read structured formats, `date` and `epoch` turn text into timestamps, and `vrl`, `gsub`, `select`, and `filter` reshape or drop what is left. - **Transform** decides how those fields are stored: the column type for each one, whether it becomes a tag, and whether it carries an `inverted`, `skipping`, or `fulltext` index. The result is an ordinary table, so a stored log is queried like any other data. A [dispatcher](/reference/pipeline/pipeline-config.md#dispatcher) can route different log types from one source into separate tables. If the incoming data is already structured, no configuration is needed: the built-in `greptime_identity` pipeline stores each field as a column. ## Getting started [Quick Start](./quick-start.md) ingests logs with the built-in `greptime_identity` pipeline. ## Send logs from a collector Each guide covers the collector's configuration and the pipeline setup it needs: - [Vector](/user-guide/ingest-data/for-observability/vector.md#using-greptimedb_logs-sink-recommended) - [Fluent Bit](/user-guide/ingest-data/for-observability/fluent-bit.md#http) - [OpenTelemetry Collector](/user-guide/ingest-data/for-observability/otel-collector.md) - [Kafka](/user-guide/ingest-data/for-observability/kafka.md#logs) - [Loki](/user-guide/ingest-data/for-observability/loki.md#using-pipeline-with-loki-push-api) - [Splunk](/user-guide/ingest-data/for-observability/splunk.md#using-a-pipeline) ## Write your own pipeline A pipeline parses a log line, transforms the extracted values, and configures the indexes on the resulting columns. - [Using Custom Pipelines](./use-custom-pipelines.md) — writing a pipeline for a log format the built-in ones do not cover. - [Managing Pipelines](./manage-pipelines.md) — creating, updating, and deleting pipelines. ## Query logs - [Full-Text Search](./fulltext-search.md) — matching text inside log messages. - [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md#logs-query) — filtering and searching from the built-in Dashboard, with a builder or a code editor. ## Reference - [Built-in Pipelines](/reference/pipeline/built-in-pipelines.md) — the pipelines GreptimeDB ships with. - [APIs for Writing Logs](/reference/pipeline/write-log-api.md) — the HTTP API that accepts log writes. - [Pipeline Configuration](/reference/pipeline/pipeline-config.md) — every processor and transform option. --- ## GreptimeDB Logs Quick Start # Quick Start This guide will walk you through the essential steps to get started with GreptimeDB's log service. You'll learn how to ingest logs using the built-in `greptime_identity` pipeline and integrate with log collectors. GreptimeDB provides a powerful pipeline-based log ingestion system. For quick setup with JSON-formatted logs, you can use the built-in `greptime_identity` pipeline, which: - Automatically handles field mapping from JSON to table columns - Creates tables automatically if they don't exist - Supports flexible schemas for varying log structures - Requires minimal configuration to get started ## Direct HTTP Ingestion The simplest way to ingest logs into GreptimeDB is through a direct HTTP request using the `greptime_identity` pipeline. For example, you can use `curl` to send a POST request with JSON log data: ```shell curl -X POST \ "http://localhost:4000/v1/ingest?db=public&table=demo_logs&pipeline_name=greptime_identity" \ -H "Content-Type: application/json" \ -H "Authorization: Basic " \ -d '[ { "timestamp": "2024-01-15T10:30:00Z", "level": "INFO", "service": "web-server", "message": "User login successful", "user_id": 12345, "ip_address": "192.168.1.100" }, { "timestamp": "2024-01-15T10:31:00Z", "level": "ERROR", "service": "database", "message": "Connection timeout occurred", "error_code": 500, "retry_count": 3 } ]' ``` The key parameters are: - `db=public`: Target database name (use your database name) - `table=demo_logs`: Target table name (created automatically if it doesn't exist) - `pipeline_name=greptime_identity`: Uses `greptime_identity` identity pipeline for JSON processing - `Authorization` header: Basic authentication with base64-encoded `username:password`, see the [HTTP Authentication Guide](/user-guide/protocols/http.md#authentication) A successful request returns: ```json { "output": [{"affectedrows": 2}], "execution_time_ms": 15 } ``` After successful ingestion, the corresponding table `demo_logs` is automatically created with columns based on the JSON fields. The schema is as follows: ```sql +--------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+---------------------+------+------+---------+---------------+ | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | ip_address | String | | YES | | FIELD | | level | String | | YES | | FIELD | | message | String | | YES | | FIELD | | service | String | | YES | | FIELD | | timestamp | String | | YES | | FIELD | | user_id | Int64 | | YES | | FIELD | | error_code | Int64 | | YES | | FIELD | | retry_count | Int64 | | YES | | FIELD | +--------------------+---------------------+------+------+---------+---------------+ ``` ## Integration with Log Collectors For production environments, you'll typically use log collectors to automatically forward logs to GreptimeDB. Here is an example about how to configure Vector to send logs to GreptimeDB using the `greptime_identity` pipeline: ```toml [sinks.my_sink_id] type = "greptimedb_logs" dbname = "public" endpoint = "http://:4000" pipeline_name = "greptime_identity" table = "" username = "" password = "" # Additional configurations as needed ``` The key configuration parameters are: - `type = "greptimedb_logs"`: Specifies the GreptimeDB logs sink - `dbname`: Target database name - `endpoint`: GreptimeDB HTTP endpoint - `pipeline_name`: Uses `greptime_identity` pipeline for JSON processing - `table`: Target table name (created automatically if it doesn't exist) - `username` and `password`: Credentials for HTTP Basic Authentication For details about the Vector configuration and options, refer to the [Vector Integration Guide](/user-guide/ingest-data/for-observability/vector.md#using-greptimedb_logs-sink-recommended). ## Next Steps You've successfully ingested your first logs, here are the recommended next steps: - **Learn more about the behaviours of built-in Pipelines**: Refer to the [Built-in Pipelines](/reference/pipeline/built-in-pipelines.md) guide for detailed information on available built-in pipelines and their configurations. - **Integrate with Popular Log Collectors**: For detailed instructions on integrating GreptimeDB with various log collectors like Fluent Bit, Fluentd, and others, refer to the [Integrate with Popular Log Collectors](./overview.md#integrate-with-log-collectors) section in the [Logs Overview](./overview.md) guide. - **Using Custom Pipelines**: To learn more about creating custom pipelines for advanced log processing and transformation, refer to the [Using Custom Pipelines](./use-custom-pipelines.md) guide. --- ## Using Custom Pipelines GreptimeDB automatically parses and transforms logs into structured, multi-column data based on your pipeline configuration. When built-in pipelines cannot handle your specific log format, you can create custom pipelines to define exactly how your log data should be parsed and transformed. ## Identify Your Original Log Format A custom pipeline is written against a specific log format, so start by determining the format of your original log data. If you're using log collectors and aren't sure about the log format, there are two ways to examine your logs: 1. **Read the collector official documentation**: Configure your collector to output data to console or file to inspect the log format. 2. **Use the `greptime_identity` pipeline**: Ingest sample logs directly into GreptimeDB using the built-in `greptime_identity` pipeline. The `greptime_identity` pipeline treats the entire text log as a single `message` field, so you can read the raw log content back from the table. Once you know the log format you want to process, you can create a custom pipeline. This document uses the following Nginx access log entry as an example: ```txt 127.0.0.1 - - [25/May/2024:20:16:37 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" ``` ## Create a Custom Pipeline GreptimeDB provides an HTTP interface for creating pipelines. Here's how to create one. First, create an example pipeline configuration file to process Nginx access logs, naming it `pipeline.yaml`: ```yaml version: 2 processors: - dissect: fields: - message patterns: - '%{ip_address} - - [%{timestamp}] "%{http_method} %{request_line}" %{status_code} %{response_size} "-" "%{user_agent}"' ignore_missing: true - date: fields: - timestamp formats: - "%d/%b/%Y:%H:%M:%S %z" - select: type: exclude fields: - message - vrl: source: | .greptime_ttl = "7d" . transform: - fields: - ip_address type: string index: inverted tag: true - fields: - status_code type: int32 index: inverted tag: true - fields: - request_line - user_agent type: string index: fulltext - fields: - response_size type: int32 - fields: - timestamp type: time index: timestamp ``` The pipeline configuration above uses the [version 2](/reference/pipeline/pipeline-config.md#transform-in-version-2) format, contains `processors` and `transform` sections that work together to structure your log data: **Processors**: Used to preprocess log data before transformation: - **Data Extraction**: The `dissect` processor uses pattern matching to parse the `message` field and extract structured data including `ip_address`, `timestamp`, `http_method`, `request_line`, `status_code`, `response_size`, and `user_agent`. - **Timestamp Processing**: The `date` processor parses the extracted `timestamp` field using the format `%d/%b/%Y:%H:%M:%S %z` and converts it to a proper timestamp data type. - **Field Selection**: The `select` processor excludes the original `message` field from the final output while retaining all other fields. - **Table Options**: The `vrl` processor sets table options on the rows it emits. Here the `.greptime_ttl = "7d"` line gives the table a 7-day time-to-live. **Transform**: Defines how to convert and index the extracted fields: - **Field Transformation**: Each extracted field is converted to its appropriate data type with specific indexing configurations. Fields like `http_method` retain their default data types when no explicit configuration is provided. - **Indexing Strategy**: - `ip_address` and `status_code` use inverted indexing as tags for fast filtering - `request_line` and `user_agent` use full-text indexing so they can be searched by keyword - `timestamp` serves as the required time index column For detailed information about pipeline configuration options, please refer to the [Pipeline Configuration](/reference/pipeline/pipeline-config.md) documentation. ## Upload the Pipeline Execute the following command to upload the pipeline configuration: ```shell curl -X "POST" \ "http://localhost:4000/v1/pipelines/nginx_pipeline" \ -H 'Authorization: Basic ' \ -F "file=@pipeline.yaml" ``` After successful execution, a pipeline named `nginx_pipeline` will be created and return the following result: ```json {"name":"nginx_pipeline","version":"2024-06-27 12:02:34.257312110Z"}. ``` You can create multiple versions for the same pipeline name. All pipelines are stored in the `greptime_private.pipelines` table. Refer to [Query Pipelines](manage-pipelines.md#query-pipelines) to view pipeline data. ## Ingest Logs Using the Pipeline The following example writes logs to the `custom_pipeline_logs` table using the `nginx_pipeline` pipeline to format and transform the log messages: ```shell curl -X POST \ "http://localhost:4000/v1/ingest?db=public&table=custom_pipeline_logs&pipeline_name=nginx_pipeline" \ -H "Content-Type: application/json" \ -H "Authorization: Basic " \ -d '[ { "message": "127.0.0.1 - - [25/May/2024:20:16:37 +0000] \"GET /index.html HTTP/1.1\" 200 612 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\"" }, { "message": "192.168.1.1 - - [25/May/2024:20:17:37 +0000] \"POST /api/login HTTP/1.1\" 200 1784 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36\"" }, { "message": "10.0.0.1 - - [25/May/2024:20:18:37 +0000] \"GET /images/logo.png HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0\"" }, { "message": "172.16.0.1 - - [25/May/2024:20:19:37 +0000] \"GET /contact HTTP/1.1\" 404 162 \"-\" \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1\"" } ]' ``` The command will return the following output upon success: ```json {"output":[{"affectedrows":4}],"execution_time_ms":79} ``` The `custom_pipeline_logs` table content is automatically created based on the pipeline configuration: ```sql +-------------+-------------+-------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+ | ip_address | http_method | status_code | request_line | user_agent | response_size | timestamp | +-------------+-------------+-------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+ | 10.0.0.1 | GET | 304 | /images/logo.png HTTP/1.1 | Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0 | 0 | 2024-05-25 20:18:37 | | 127.0.0.1 | GET | 200 | /index.html HTTP/1.1 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 | 612 | 2024-05-25 20:16:37 | | 172.16.0.1 | GET | 404 | /contact HTTP/1.1 | Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1 | 162 | 2024-05-25 20:19:37 | | 192.168.1.1 | POST | 200 | /api/login HTTP/1.1 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 | 1784 | 2024-05-25 20:17:37 | +-------------+-------------+-------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+ ``` For more detailed information about the log ingestion API endpoint `/ingest`, including additional parameters and configuration options, please refer to the [APIs for Writing Logs](/reference/pipeline/write-log-api.md) documentation. ## Query Logs We use the `custom_pipeline_logs` table as an example to query logs. ### Query logs by tags With the multiple tag columns in `custom_pipeline_logs`, you can query data by tags flexibly. For example, query the logs with `status_code` 200 and `http_method` GET. ```sql SELECT * FROM custom_pipeline_logs WHERE status_code = 200 AND http_method = 'GET'; ``` ```sql +------------+-------------+----------------------+---------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ | ip_address | status_code | request_line | user_agent | response_size | timestamp | http_method | +------------+-------------+----------------------+---------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ | 127.0.0.1 | 200 | /index.html HTTP/1.1 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 | 612 | 2024-05-25 20:16:37 | GET | +------------+-------------+----------------------+---------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ 1 row in set (0.02 sec) ``` ### Full‑Text Search For the text fields `request_line` and `user_agent`, you can use `matches_term` function to search logs. Remember, we created the full-text index for these two columns when [creating a pipeline](#create-a-custom-pipeline). This allows for high-performance full-text searches. For example, query the logs with `request_line` containing `/index.html` or `/api/login`. ```sql SELECT * FROM custom_pipeline_logs WHERE matches_term(request_line, '/index.html') OR matches_term(request_line, '/api/login'); ``` ```sql +-------------+-------------+----------------------+--------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ | ip_address | status_code | request_line | user_agent | response_size | timestamp | http_method | +-------------+-------------+----------------------+--------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ | 127.0.0.1 | 200 | /index.html HTTP/1.1 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 | 612 | 2024-05-25 20:16:37 | GET | | 192.168.1.1 | 200 | /api/login HTTP/1.1 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 | 1784 | 2024-05-25 20:17:37 | POST | +-------------+-------------+----------------------+--------------------------------------------------------------------------------------------------------------------------+---------------+---------------------+-------------+ 2 rows in set (0.00 sec) ``` You can refer to the [Full-Text Search](fulltext-search.md) document for detailed usage of the `matches_term` function. ## Benefits of Using Pipelines Using pipelines to process logs provides structured data and automatic field extraction, enabling more efficient querying and analysis. You can also write logs directly to the database without pipelines, but this approach limits high-performance analysis capabilities. ### Direct Log Insertion (Without Pipeline) For comparison, you can create a table to store original log messages: ```sql CREATE TABLE `origin_logs` ( `message` STRING FULLTEXT INDEX, `time` TIMESTAMP TIME INDEX ) WITH ( append_mode = 'true' ); ``` Use the `INSERT` statement to insert logs into the table. Note that you need to manually add a timestamp field for each log: ```sql INSERT INTO origin_logs (message, time) VALUES ('127.0.0.1 - - [25/May/2024:20:16:37 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"', '2024-05-25 20:16:37.217'), ('192.168.1.1 - - [25/May/2024:20:17:37 +0000] "POST /api/login HTTP/1.1" 200 1784 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"', '2024-05-25 20:17:37.217'), ('10.0.0.1 - - [25/May/2024:20:18:37 +0000] "GET /images/logo.png HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"', '2024-05-25 20:18:37.217'), ('172.16.0.1 - - [25/May/2024:20:19:37 +0000] "GET /contact HTTP/1.1" 404 162 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1"', '2024-05-25 20:19:37.217'); ``` ### Schema Comparison: Pipeline vs Raw In the above examples, the table `custom_pipeline_logs` is automatically created by writing logs using pipeline, and the table `origin_logs` is created by writing logs directly. Let's explore the differences between these two tables. ```sql DESC custom_pipeline_logs; ``` ```sql +---------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------+---------------------+------+------+---------+---------------+ | ip_address | String | PRI | YES | | TAG | | status_code | Int32 | PRI | YES | | TAG | | request_line | String | | YES | | FIELD | | user_agent | String | | YES | | FIELD | | response_size | Int32 | | YES | | FIELD | | timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | http_method | String | | YES | | FIELD | +---------------+---------------------+------+------+---------+---------------+ 7 rows in set (0.00 sec) ``` ```sql DESC origin_logs; ``` ```sql +---------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------+----------------------+------+------+---------+---------------+ | message | String | | YES | | FIELD | | time | TimestampMillisecond | PRI | NO | | TIMESTAMP | +---------+----------------------+------+------+---------+---------------+ ``` Comparing the table structures shows the key differences: The `custom_pipeline_logs` table (created with pipeline) automatically structures log data into multiple columns: - `ip_address`, `status_code` as indexed tags for fast filtering - `request_line`, `user_agent` with full-text indexing for text search - `response_size`, `http_method` as regular fields - `timestamp` as the time index The `origin_logs` table (direct insertion) stores everything in a single `message` column. ### Why Use Pipelines? It is recommended to use the pipeline method to split the log message into multiple columns, which offers the advantage of explicitly querying specific values within certain columns. Column matching query proves superior to full-text searching for several key reasons: - **Performance**: Column-based queries are typically faster than full-text searches - **Storage Efficiency**: GreptimeDB's columnar storage compresses structured data better; inverted indexes for tags consume less storage than full-text indexes - **Query Simplicity**: Tag-based queries are easier to write, understand, and debug ## Next Steps - **Full-Text Search**: Explore the [Full-Text Search](fulltext-search.md) guide to learn advanced text search capabilities and query techniques in GreptimeDB - **Pipeline Configuration**: Explore the [Pipeline Configuration](/reference/pipeline/pipeline-config.md) documentation to learn more about creating and customizing pipelines for various log formats and processing needs --- ## Data Index GreptimeDB provides various indexing mechanisms to accelerate query performance. Indexes are essential database structures that help optimize data retrieval operations by creating efficient lookup paths to specific data. ## Overview Indexes in GreptimeDB are specified during table creation and are designed to improve query performance for different types of data and query patterns. The database currently supports these types of indexes: - Inverted Index - Skipping Index - Fulltext Index Notice that in this chapter we are narrowing the word "index" to those related to data value indexing. PRIMARY KEY and TIME INDEX can also be treated as index in some scenarios, but they are not covered here. ## Index Types ### Inverted Index An inverted index is particularly useful for tag columns. It creates a mapping between unique tag values and their corresponding rows, enabling fast lookups for specific tag values. The inverted index is not automatically applied to tag columns. You need to manually create an inverted index by considering the following typical use cases: - Querying data by tag values - Filtering operations on string columns - Point queries on tag columns Example: ```sql CREATE TABLE monitoring_data ( host STRING INVERTED INDEX, `region` STRING PRIMARY KEY INVERTED INDEX, cpu_usage DOUBLE, `timestamp` TIMESTAMP TIME INDEX, ); ``` However, when a column has very high cardinality, the inverted index may not be the best choice due to the overhead of maintaining the index. It may bring high memory consumption and large index size. In this case, you may consider using the skipping index. ### Skipping Index Skipping index suits for columnar data systems like GreptimeDB. It maintains metadata about value ranges within data blocks, allowing the query engine to skip irrelevant data blocks during range queries efficiently. This index also has smaller size compare to others. **Use Cases:** - When certain values are sparse, such as MAC address codes in logs. - Querying specific values that occur infrequently within large datasets Example: ```sql CREATE TABLE sensor_data ( `domain` STRING PRIMARY KEY, device_id STRING SKIPPING INDEX, temperature DOUBLE, `timestamp` TIMESTAMP TIME INDEX, ); ``` Skipping index supports options by `WITH`: * `type`: The index type, only supports `BLOOM` type right now. * `granularity`: (For `BLOOM` type) The size of data chunks covered by each filter. A smaller granularity improves filtering but increases index size. Default is `10240`. * `false_positive_rate`: (For `BLOOM` type) The probability of misidentifying a block. A lower rate improves accuracy (better filtering) but increases index size. Value is a float between `0` and `1`. Default is `0.01`. For example: ```sql CREATE TABLE sensor_data ( `domain` STRING PRIMARY KEY, device_id STRING SKIPPING INDEX WITH(type='BLOOM', granularity=1024, false_positive_rate=0.01), temperature DOUBLE, `timestamp` TIMESTAMP TIME INDEX, ); ``` Skipping index can't handle complex filter conditions, and usually has a lower filtering performance compared to inverted index or full-text index. ### Full-Text Index Full-text index is designed for text search operations on string columns. It enables efficient searching of text content using word-based matching and text search capabilities. You can query text data with flexible keywords, phrases, or pattern matching queries. **Use Cases:** - Text search operations - Pattern matching queries - Large text filtering Example: ```sql CREATE TABLE logs ( `message` STRING FULLTEXT INDEX, `level` STRING PRIMARY KEY, `timestamp` TIMESTAMP TIME INDEX, ); ``` #### Configuration Options When creating or modifying a full-text index, you can specify the following options using `FULLTEXT INDEX WITH`: - `analyzer`: Sets the language analyzer for the full-text index - Supported values: `English`, `Chinese` - Default: `English` - Note: The Chinese analyzer requires significantly more time to build the index due to the complexity of Chinese text segmentation. Consider using it only when Chinese text search is a primary requirement. - `case_sensitive`: Determines whether the full-text index is case-sensitive - Supported values: `true`, `false` - Default: `false` - Note: Setting to `true` may slightly improve performance for case-sensitive queries, but will degrade performance for case-insensitive queries. This setting does not affect the results of `matches_term` queries. - `backend`: Sets the backend for the full-text index - Supported values: `bloom`, `tantivy` - Default: `bloom` - `granularity`: (For `bloom` backend) The size of data chunks covered by each filter. A smaller granularity improves filtering but increases index size. - Supported values: positive integer - Default: `10240` - `false_positive_rate`: (For `bloom` backend) The probability of misidentifying a block. A lower rate improves accuracy (better filtering) but increases index size. - Supported values: float between `0` and `1` - Default: `0.01` #### Backend Selection GreptimeDB provides two full-text index backends for efficient log searching: :::note The index sizes and performance multipliers below come from Greptime's testing. Use them to compare the two backends against each other, not as absolute figures for your data: selectivity, term distribution, and cache state all move these numbers. ::: 1. **Bloom Backend** - Best for: General-purpose log searching - Features: - Uses Bloom filter for efficient filtering - Lower storage overhead - Consistent performance across different query patterns - Limitations: - Slightly slower for high-selectivity queries - Storage Cost Example: - Original data: ~10GB - Bloom index: ~1GB 2. **Tantivy Backend** - Best for: High-selectivity queries (e.g., unique values like TraceID) - Features: - Uses inverted index for fast exact matching - Excellent performance for high-selectivity queries - Limitations: - Higher storage overhead (close to original data size) - Slower performance for low-selectivity queries - Storage Cost Example: - Original data: ~10GB - Tantivy index: ~10GB #### Performance Comparison The following table shows the performance comparison between different query methods (using Bloom as baseline): | Query Type | High Selectivity (e.g., TraceID) | Low Selectivity (e.g., "HTTP") | |------------|----------------------------------|--------------------------------| | LIKE | 50x slower | 1x | | Tantivy | 5x faster | 5x slower | | Bloom | 1x (baseline) | 1x (baseline) | Key observations: - For high-selectivity queries (e.g., unique values), Tantivy provides the best performance - For low-selectivity queries, Bloom offers more consistent performance - Bloom has significant storage advantage over Tantivy (1GB vs 10GB in test case) #### Examples **Creating a Table with Full-Text Index** ```sql -- Using Bloom backend (recommended for most cases) CREATE TABLE logs ( timestamp TIMESTAMP(9) TIME INDEX, `message` STRING FULLTEXT INDEX WITH ( backend = 'bloom', analyzer = 'English', case_sensitive = 'false' ) ); -- Using Tantivy backend (for high-selectivity queries) CREATE TABLE logs ( timestamp TIMESTAMP(9) TIME INDEX, `message` STRING FULLTEXT INDEX WITH ( backend = 'tantivy', analyzer = 'English', case_sensitive = 'false' ) ); ``` **Modifying an Existing Table** ```sql -- Enable full-text index on an existing column ALTER TABLE monitor MODIFY COLUMN load_15 SET FULLTEXT INDEX WITH ( analyzer = 'English', case_sensitive = 'false', backend = 'bloom' ); -- Change full-text index configuration ALTER TABLE logs MODIFY COLUMN message SET FULLTEXT INDEX WITH ( analyzer = 'English', case_sensitive = 'false', backend = 'tantivy' ); ``` Fulltext index usually comes with following drawbacks: - Higher storage overhead compared to regular indexes due to storing word tokens and positions - Increased flush and compaction latency as each text document needs to be tokenized and indexed - May not be optimal for simple prefix or suffix matching operations Consider using full-text index only when you need advanced text search capabilities and flexible query patterns. ## Modify indexes You can always change the index type of columns by the `ALTER TABLE` statement, read the [reference](/reference/sql/alter.md#alter-table) for more info. ## Best Practices 1. Choose the appropriate index type based on your data type and query patterns 2. Index only the columns that are frequently used in WHERE clauses 3. Consider the trade-off between query performance, ingest performance and resource consumption 4. Monitor index usage and performance to optimize your indexing strategy continuously ## Performance Considerations While indexes can significantly improve query performance, they come with some overhead: - Additional storage space required for index structures - Impact on flush and compaction performance due to index maintenance - Memory usage for index caching Choose indexes carefully based on your specific use case and performance requirements. --- ## Update, Delete, and TTL # Update, Delete, and TTL GreptimeDB has no `UPDATE` statement. Rows are updated by inserting over them, and how that insert behaves depends on the table's merge mode. This page covers updating, deleting, and expiring data. Table and Region maintenance — sharding, migration, compaction — is in [Table and Region Operations](/user-guide/deployments-administration/manage-data/overview.md). ## Update data ### Update data with same tags and time index Updates can be efficiently performed by inserting new data. If rows of data have the same tags and time index, the old data will be replaced with the new data. This means that you can only update columns with a field type. Column types are described in [Data Model](../concepts/data-model.md). :::warning Note Excessive updates may negatively impact query performance, even though the performance of updates is the same as insertion. ::: #### Update all fields in a table By default, when updating data, all fields will be overwritten with the new values, except for [InfluxDB line protocol](/user-guide/protocols/influxdb-line-protocol.md), which only [updates the specified fields](#update-specific-fields-in-a-table). The following example using SQL demonstrates the behavior of overwriting all fields in a table. Assuming you have a table named `monitor` with the following schema. The `host` column represents the tag and the `ts` column represents the time index. ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64, memory FLOAT64, PRIMARY KEY(host) ); ``` Insert a new row into the `monitor` table: ```sql INSERT INTO monitor (host, ts, cpu, memory) VALUES ("127.0.0.1", "2024-07-11 20:00:00", 0.8, 0.1); ``` Check the data in the table: ```sql SELECT * FROM monitor; ``` ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-07-11 20:00:00 | 0.8 | 0.1 | +-----------+---------------------+------+--------+ 1 row in set (0.00 sec) ``` To update the data, you can use the same `host` and `ts` values as the existing data and set the new `cpu` value to `0.5`: ```sql INSERT INTO monitor (host, ts, cpu, memory) -- The same tag `127.0.0.1` and the same time index 2024-07-11 20:00:00 VALUES ("127.0.0.1", "2024-07-11 20:00:00", 0.5, 0.1); ``` The new data will be: ```sql SELECT * FROM monitor; ``` ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-07-11 20:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ 1 row in set (0.01 sec) ``` With the default merge policy, if columns are omitted in the `INSERT INTO` statement, they will be overwritten with the default values. For example: ```sql INSERT INTO monitor (host, ts, cpu) VALUES ("127.0.0.1", "2024-07-11 20:00:00", 0.5); ``` The default value of the `memory` column in the `monitor` table is `NULL`. Therefore, the new data will be: ```sql SELECT * FROM monitor; ``` ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-07-11 20:00:00 | 0.5 | NULL | +-----------+---------------------+------+--------+ 1 row in set (0.01 sec) ``` ### Update specific fields in a table This update policy is supported by default in the [InfluxDB line protocol](/user-guide/protocols/influxdb-line-protocol.md). You can also enable this behavior by specifying the `merge_mode` option as `last_non_null` when creating a table using SQL. Here's an example: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64, memory FLOAT64, PRIMARY KEY(host) ) WITH ('merge_mode'='last_non_null'); ``` ```sql INSERT INTO monitor (host, ts, cpu, memory) VALUES ("127.0.0.1", "2024-07-11 20:00:00", 0.8, 0.1); ``` To update specific fields in the `monitor` table, you can insert new data with only the fields you want to update. For example: ```sql INSERT INTO monitor (host, ts, cpu) VALUES ("127.0.0.1", "2024-07-11 20:00:00", 0.5); ``` This will update the `cpu` field while leaving the `memory` field unchanged. The result will be: ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-07-11 20:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ ``` Notice that the `last_non_null` merge mode cannot update the old value to `NULL`. For example: ```sql INSERT INTO monitor (host, ts, cpu, memory) VALUES ("127.0.0.1", "2024-07-11 20:00:01", 0.8, 0.1); ``` ```sql INSERT INTO monitor (host, ts, cpu) VALUES ("127.0.0.1", "2024-07-11 20:00:01", NULL); ``` That will not update anything: ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-07-11 20:00:01 | 0.8 | 0.1 | +-----------+---------------------+------+--------+ ``` See [CREATE TABLE](/reference/sql/create.md#create-a-table-with-merge-mode) for the `merge_mode` option. ### Avoid updating data by creating table with `append_mode` option GreptimeDB supports an `append_mode` option when creating a table, which always inserts new data to the table. This is especially useful when you want to keep all historical data, such as logs. You can only create a table with the `append_mode` option using SQL. After successfully creating the table, all protocols [ingest data](/user-guide/ingest-data/overview.md) to the table will always insert new data. For example, you can create an `app_logs` table with the `append_mode` option as follows. The `host` and `log_level` columns represent tags, and the `ts` column represents the time index. ```sql CREATE TABLE app_logs ( ts TIMESTAMP TIME INDEX, host STRING, api_path STRING FULLTEXT INDEX, log_level STRING, `log` STRING FULLTEXT INDEX, PRIMARY KEY (host, log_level) ) WITH ('append_mode'='true'); ``` Insert a new row into the `app_logs` table: ```sql INSERT INTO app_logs (ts, host, api_path, log_level, `log`) VALUES ('2024-07-11 20:00:10', 'host1', '/api/v1/resource', 'ERROR', 'Connection timeout'); ``` Check the data in the table: ```sql SELECT * FROM app_logs; ``` The output will be: ```sql +---------------------+-------+------------------+-----------+--------------------+ | ts | host | api_path | log_level | log | +---------------------+-------+------------------+-----------+--------------------+ | 2024-07-11 20:00:10 | host1 | /api/v1/resource | ERROR | Connection timeout | +---------------------+-------+------------------+-----------+--------------------+ 1 row in set (0.01 sec) ``` You can insert new data with the same tag and time index: ```sql INSERT INTO app_logs (ts, host, api_path, log_level, `log`) -- The same tag `host1` and `ERROR`, the same time index 2024-07-11 20:00:10 VALUES ('2024-07-11 20:00:10', 'host1', '/api/v1/resource', 'ERROR', 'Connection reset'); ``` Then you will find two rows in the table: ```sql SELECT * FROM app_logs; ``` ```sql +---------------------+-------+------------------+-----------+--------------------+ | ts | host | api_path | log_level | log | +---------------------+-------+------------------+-----------+--------------------+ | 2024-07-11 20:00:10 | host1 | /api/v1/resource | ERROR | Connection reset | | 2024-07-11 20:00:10 | host1 | /api/v1/resource | ERROR | Connection timeout | +---------------------+-------+------------------+-----------+--------------------+ 2 rows in set (0.01 sec) ``` ## Delete Data You can effectively delete data by specifying tags and time index. Deleting data without specifying the tag and time index columns is not efficient, as it requires two steps: querying the data and then deleting it by tag and time index. Column types are described in [Data Model](../concepts/data-model.md). :::warning Warning Excessive deletions can negatively impact query performance. ::: You can only delete data using SQL. For example, to delete a row from the `monitor` table with tag `host` and timestamp index `ts`: ```sql DELETE FROM monitor WHERE host='127.0.0.2' AND ts=1667446798450; ``` The output will be: ```sql Query OK, 1 row affected (0.00 sec) ``` See [DELETE](/reference/sql/delete.md) for the full statement reference. ## Truncate Table To delete all data in a table, you can use the `TRUNCATE TABLE` statement in SQL. For example, to truncate the `monitor` table: ```sql TRUNCATE TABLE monitor; ``` See [TRUNCATE TABLE](/reference/sql/truncate.md) for the full statement reference. ## Manage data retention with TTL policies You can use Time to Live (TTL) policies to automatically remove stale data from your databases. TTL allows you to set policies to periodically delete data from tables. Setting TTL policies has the following benefits: - Decrease storage costs by cleaning out obsolete data. - Reduce the number of rows the database has to scan for some queries, potentially increasing query performance. > Data past its TTL is not removed at the moment it expires. Instead, they are deleted during the compaction, which is a background job run asynchronously. > If you are testing the TTL policy, be sure to trigger data flush and compaction before querying the data. > You can use our "[ADMIN](/reference/sql/admin.md)" functions to manually run them. You can set TTL for every table when creating it. For example, the following SQL statement creates a table named `monitor` with a TTL policy of 7 days: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, cpu FLOAT64, memory FLOAT64, PRIMARY KEY(host) ) WITH ('ttl'='7d'); ``` You can also create a database-level TTL policy. For example, the following SQL statement creates a database named `test` with a TTL policy of 7 days: ```sql CREATE DATABASE test WITH ('ttl'='7d'); ``` You can set TTL policies at both the table level and the database level simultaneously. If a table has its own TTL policy, it will take precedence over the database TTL policy. Otherwise, the database TTL policy will be applied to the table. The value of `'ttl'` can be one of duration (like `1hour 12min 5s`), `instant` or `forever`. See details in [CREATE](/reference/sql/create.md#create-a-table-with-ttl) statement. Use [`ALTER`](/reference/sql/alter.md#alter-table-options) to modify the TTL of an existing table or database: ```sql -- for table ALTER TABLE monitor SET 'ttl'='1 month'; -- for database ALTER DATABASE test SET 'ttl'='1 month'; ``` If you want to remove the TTL policy, you can use the following SQL ```sql -- for table ALTER TABLE monitor UNSET 'ttl'; -- or for database ALTER DATABASE test UNSET 'ttl'; ``` See [CREATE](/reference/sql/create.md) for the full TTL syntax. ## More data management operations Basic table operations, table sharding, and Region migration are covered in [Table and Region Operations](/user-guide/deployments-administration/manage-data/overview.md). --- ## Migrate from ClickHouse This guide provides a detailed explanation on how to smoothly migrate your business from ClickHouse to GreptimeDB. It covers pre-migration preparation, data model adjustments, table structure reconstruction, dual-write assurance, and specific methods for data export and import, aiming to achieve a seamless system transition. ## Pre-Migration Notes - **Compatibility** Although GreptimeDB is SQL protocol compatible, the two databases have fundamental differences in data modeling, index design, and compression mechanisms. Be sure to refer to the [SQL compatibility](/reference/sql/compatibility.md) documentation and official [modeling guidelines](/user-guide/deployments-administration/performance-tuning/design-table.md) to refactor table structures and data flows during migration. - **Data Model Differences** ClickHouse is a general-purpose big data analytics engine, while GreptimeDB is optimized for time-series, metrics, and log observability scenarios. There are differences in their data models, index systems, and compression algorithms, so it’s important to take these differences and the actual business scenario into account during model design and compatibility considerations. --- ## Refactoring Data Model and Table Structure ### Time Index - ClickHouse tables do not always have a `time index` field. During migration, you need to clearly select the primary time field for your business to serve as the time index and specify it when creating the table in GreptimeDB. For example, typical log or trace print times. - The time precision (such as second, millisecond, microsecond, etc.) should be assessed based on real-world requirements and cannot be changed once set. ### Primary Key and Wide Table Recommendations - Primary Key: Just like the `order by` in ClickHouse without the timestamp column. It’s not recommended to include high-cardinality fields such as log IDs, user IDs or UUIDs to avoid primary key bloat, excessive write amplification, and inefficient queries. - Wide Table vs. Multiple Tables: For multiple metrics collected at the same observation point (such as on the same host), it’s better to use a wide table, which improves batch write efficiency and compression ratio. ### Index Planning - Inverted Index: Build indexes for low-cardinality columns to improve filter efficiency. - Skipping Index: Use as needed; Used when certain values are sparse or querying specific values that occur infrequently within large datasets. - Fulltext Index: Use as needed; Designed for text search operations on string columns; avoid building unnecessary indexes on high-cardinality or highly variable fields. - Read [Data Index](/user-guide/manage-data/data-index.md) for more info. ### Partitioning Table ClickHouse supports table partitioning via the `PARTITION BY` syntax. GreptimeDB provides a similar feature with a different syntax; see the [table sharding](/user-guide/deployments-administration/manage-data/table-sharding.md) documentation for details. ### TTL GreptimeDB supports TTL (Time-to-live) through the table option `ttl`. For more information, please refer to [Manage data retention with TTL policies](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies). ### Example Table Example ClickHouse table structure: ```sql CREATE TABLE example ( timestamp DateTime, host String, app String, metric String, value Float64 ) ENGINE = MergeTree() TTL timestamp + INTERVAL 30 DAY ORDER BY (timestamp, host, app, metric); ``` Recommended table structure after migrating to GreptimeDB: ```sql CREATE TABLE example ( `timestamp` TIMESTAMP NOT NULL, host STRING, app STRING INVERTED INDEX, metric STRING INVERTED INDEX, `value` DOUBLE, PRIMARY KEY (host, app, metric), TIME INDEX (`timestamp`) ) with(ttl='30d'); ``` > The choice of primary key and the granularity of the time index should be carefully planned based on your business's data volume and query scenarios. If the host cardinality is high (e.g., hundreds of thousands of monitored hosts), you should remove it from the primary key and consider creating a skipping index for it. --- ### Migrating Typical Log Tables > GreptimeDB already provides built-in modeling for otel log ingestion, so please refer to the [official documentation](/user-guide/ingest-data/for-observability/opentelemetry.md#logs). Common ClickHouse log table structure: ```sql CREATE TABLE logs ( timestamp DateTime, host String, service String, log_level String, log_message String, trace_id String, span_id String, INDEX inv_idx(log_message) TYPE ngrambf_v1(4, 1024, 1, 0) GRANULARITY 1 ) ENGINE = MergeTree ORDER BY (timestamp, host, service); ``` Recommended GreptimeDB table structure: - Time index: `timestamp` (precision set based on logging frequency) - Primary key: `host`, `service` (fields often used in queries/aggregations) - Field columns: `log_message`, `trace_id`, `span_id` (high-cardinality, unique identifiers, or raw content) ```sql CREATE TABLE logs ( `timestamp` TIMESTAMP NOT NULL, host STRING, service STRING, log_level STRING, log_message STRING FULLTEXT INDEX WITH ( backend = 'bloom', analyzer = 'English', case_sensitive = 'false' ), trace_id STRING SKIPPING INDEX, span_id STRING SKIPPING INDEX, PRIMARY KEY (host, service), TIME INDEX (`timestamp`) ); ``` **Notes:** - `host` and `service` serve as common query filters and are included in the primary key to optimize filtering. If there are very many hosts, you might not want to include `host` in the primary key but instead create a skip index. - `log_message` is treated as raw content with a full-text index created. If you want the full-text index to take effect during queries, you also need to adjust your SQL query syntax. Please refer to [the log query documentation](/user-guide/logs/fulltext-search.md) for details - Since `trace_id` and `span_id` are mostly high-cardinality fields, it is not recommended to use them in the primary key, but skip indexes have been added. --- ### Migrating Typical Traces Tables > GreptimeDB also provides built-in modeling for otel trace ingestion, please read the [official documentation](/user-guide/ingest-data/for-observability/opentelemetry.md#traces). Common ClickHouse trace table structure design: ```sql CREATE TABLE traces ( timestamp DateTime, trace_id String, span_id String, parent_span_id String, service String, operation String, duration UInt64, status String, tags Map(String, String) ) ENGINE = MergeTree() ORDER BY (timestamp, trace_id, span_id); ``` Recommended GreptimeDB table structure: - Time index: `timestamp` (e.g., collection/start time) - Primary key: `service`, `operation` (commonly filtered/aggregated properties) - Field columns: `trace_id`, `span_id`, `parent_span_id`, `duration`, `tags` (high-cardinality or Map type) ```sql CREATE TABLE traces ( `timestamp` TIMESTAMP NOT NULL, service STRING, operation STRING, `status` STRING, trace_id STRING SKIPPING INDEX, span_id STRING SKIPPING INDEX, parent_span_id STRING SKIPPING INDEX, duration DOUBLE, tags STRING, -- If this is structured JSON, either store it as-is or use the pipeline to parse fields PRIMARY KEY (service, operation), TIME INDEX (`timestamp`) ); ``` **Notes:** - `service` and `operation` serve as primary key, supporting trace scheduling and aggregate queries by service or operations. - `trace_id`, `span_id`, and `parent_span_id` use skip indexes but are not part of the primary key. - High-cardinality fields are set as fields for efficient writes. For complex properties like `tags`, [JSON storage](/reference/sql/data-types.md#json-type-experimental) or string is recommended, and they can be expanded using GreptimeDB’s ETL - [Pipeline](/user-guide/logs/use-custom-pipelines.md) if necessary. - Depending on overall business volume, consider whether to partition traces into multiple tables (such as in massive multi-service environments). --- ## Dual-write Strategy for Safe Migration During the migration, have the application write to both ClickHouse and GreptimeDB so the two systems run in parallel and you can compare them before cutting over. Dual-write is not a transaction across the two databases. Each write can succeed on one side and fail on the other, which leaves a gap or a duplicate rather than an identical copy. Plan for that: - Record every failed write with enough context to replay it, and retry each destination independently. Check that the row is actually missing before replaying it: an [append-only table](/user-guide/manage-data/overview.md#avoid-updating-data-by-creating-table-with-append_mode-option) keeps duplicate rows instead of merging them by primary key and time index, so retrying a write that did land leaves a second copy. - Fix a cutoff — a timestamp or a source offset — that separates what the historical export covers from what dual-write covers, so the two do not overlap or leave a hole between them. - Reconcile before switching over: compare row counts and aggregates per time window rather than assuming the two sides match. --- ## Exporting and Importing Historical Data 1. **Enable dual-write, then fix a cutoff** Start writing to both ClickHouse and GreptimeDB, and note the instant dual-write became live. Use **that same instant** as the cutoff `C` for every export below, so the historical export covers `timestamp < C` and dual-write covers everything from `C` onward. Do not pick a cutoff later than the moment dual-write started: rows written in between would be delivered by dual-write *and* matched by the export, and would arrive twice unless the destination table deduplicates on its primary key and time index. This split also assumes rows carry a timestamp at or after the moment they are written; late or backfilled data with older timestamps still lands on both sides. 2. **Data export from ClickHouse** Use ClickHouse’s native command to export data as CSV, TSV, Parquet, or other formats. For example: ```sh clickhouse client --query="SELECT * FROM example WHERE timestamp < '2024-04-26 00:00:00' INTO OUTFILE 'example.csv' FORMAT CSVWithNames" ``` The exported CSV will look like: ```csv "timestamp","host","app","metric","value" "2024-04-25 10:00:00","host01","nginx","cpu_usage",12.7 "2024-04-25 10:00:00","host02","redis","cpu_usage",8.4 "2024-04-25 10:00:00","host03","postgres","cpu_usage",15.3 "2024-04-25 10:01:00","host01","nginx","cpu_usage",12.5 "2024-04-25 10:01:00","host01","nginx","mem_usage",1034.5 "2024-04-25 10:01:00","host02","redis","mem_usage",876.2 "2024-04-25 10:01:00","host03","postgres","mem_usage",1145.2 "2024-04-25 10:02:00","host01","nginx","disk_io",120.3 "2024-04-25 10:02:00","host02","redis","disk_io",95.1 "2024-04-25 10:02:00","host03","postgres","disk_io",134.7 "2024-04-25 10:03:00","host02","redis","mem_usage",874 "2024-04-25 10:04:00","host03","postgres","cpu_usage",15.1 ``` 3. **Data import into GreptimeDB** > The table must be created in GreptimeDB before importing data. GreptimeDB currently supports batch data import via SQL commands or [REST API](/user-guide/protocols/http.md#post-sql-statements). For large datasets, import in batches. Use the [`COPY FROM` command](/reference/sql/copy.md#copy-from) to import: ```sql COPY example FROM "/path/to/example.csv" WITH (FORMAT = 'CSV'); ``` Alternatively, you can convert the CSV to standard INSERT statements for batch import. --- ## Validation and Cutover - Once the import is complete, use GreptimeDB’s query interface to compare data with ClickHouse for consistency. - After data verification and monitoring meet requirements, you can officially switch business writes to GreptimeDB and disable dual-write mode. --- ## Frequently Asked Questions and Optimization Tips ### What if SQL/types are incompatible? Before migration, audit all query SQL and rewrite or translate as necessary, referring to the [official documentation](/user-guide/query-data/sql.md) (especially for [log query](/user-guide/logs/fulltext-search.md)) for any incompatible syntax or data types. ### How do I efficiently import very large datasets in batches? For large tables or full historical data, export and import by partition or shard as appropriate. Monitor write speed and import progress closely. ### How should high-cardinality fields be handled? Avoid using high-cardinality fields as primary key. Store them as fields instead, and split into multiple tables if necessary. ### How should wide tables be planned? For each monitoring entity or collection endpoint, consolidate all metrics into a single table. For example, use a `host_metrics` table to store all server statistics. --- If you need a more detailed migration plan or example scripts, please provide the specific table structure and data volume. The [GreptimeDB official community](https://github.com/orgs/GreptimeTeam/discussions) will offer further support. Welcome to join the [Greptime Slack](http://greptime.com/slack). --- ## Migrate from InfluxDB If you use an AI coding agent to migrate queries, give it the [`influxql-to-greptimedb-sql` Skill](/faq-and-others/vibecoding.md#greptimedb-skills) to convert InfluxQL to GreptimeDB SQL and identify semantic differences. ```shell curl -X POST 'http://:4000/v1/influxdb/api/v2/write?bucket=' \ -H 'authorization: token ' \ -d 'census,location=klamath,scientist=anderson bees=23 1566086400000000000' ``` ```shell curl 'http://:4000/v1/influxdb/write?db=&u=&p=' \ -d 'census,location=klamath,scientist=anderson bees=23 1566086400000000000' ``` For detailed configuration instructions, please refer to the [Ingest Data via Telegraf](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md#telegraf) documentation. ```js [Node.js] 'use strict' /** @module write **/ /** Environment variables **/ const url = 'http://:4000/v1/influxdb' const token = ':' const org = '' const bucket = '' const influxDB = new InfluxDB({ url, token }) const writeApi = influxDB.getWriteApi(org, bucket) writeApi.useDefaultTags({ region: 'west' }) const point1 = new Point('temperature') .tag('sensor_id', 'TLM01') .floatField('value', 24.0) writeApi.writePoint(point1) // The write API buffers points into batches, so nothing is sent until the // buffer is flushed. close() flushes the remaining data and cancels pending // retries; without it, buffered points can be lost. try { await writeApi.close() } catch (e) { console.error('write failed', e) process.exitCode = 1 } ``` ```python from influxdb_client.client.write_api import SYNCHRONOUS bucket = "" org = "" token = ":" url="http://:4000/v1/influxdb" client = influxdb_client.InfluxDBClient( url=url, token=token, org=org ) # Write script write_api = client.write_api(write_options=SYNCHRONOUS) p = influxdb_client.Point("my_measurement").tag("location", "Prague").field("temperature", 25.3) write_api.write(bucket=bucket, org=org, record=p) ``` ```go bucket := "" org := "" token := ":" url := "http://:4000/v1/influxdb" client := influxdb2.NewClient(url, token) writeAPI := client.WriteAPIBlocking(org, bucket) p := influxdb2.NewPoint("stat", map[string]string{"unit": "temperature"}, map[string]interface{}{"avg": 24.5, "max": 45}, time.Now()) writeAPI.WritePoint(context.Background(), p) client.Close() ``` ```java private static String url = "http://:4000/v1/influxdb"; private static String org = ""; private static String bucket = ""; private static char[] token = ":".toCharArray(); public static void main(final String[] args) { InfluxDBClient influxDBClient = InfluxDBClientFactory.create(url, token, org, bucket); WriteApiBlocking writeApi = influxDBClient.getWriteApiBlocking(); Point point = Point.measurement("temperature") .addTag("location", "west") .addField("value", 55D) .time(Instant.now().toEpochMilli(), WritePrecision.MS); writeApi.writePoint(point); influxDBClient.close(); } ``` ```php $client = new Client([ "url" => "http://:4000/v1/influxdb", "token" => ":", "bucket" => "", "org" => "", "precision" => InfluxDB2\Model\WritePrecision::S ]); $writeApi = $client->createWriteApi(); $dateTimeNow = new DateTime('NOW'); $point = Point::measurement("weather") ->addTag("location", "Denver") ->addField("temperature", rand(0, 20)) ->time($dateTimeNow->getTimestamp()); $writeApi->write($point); ``` It is recommended using Grafana to visualize data in GreptimeDB. Please refer to the [Grafana documentation](/user-guide/integrations/grafana.md) for details on configuring GreptimeDB. ```shell for file in data.*; do if ! curl -sS --fail-with-body --retry 3 \ -X POST "http://${GREPTIME_HOST}:4000/v1/influxdb/write?db=${GREPTIME_DB}&u=${GREPTIME_USERNAME}&p=${GREPTIME_PASSWORD}" \ --data-binary @"${file}"; then echo "import failed on ${file}" >&2 exit 1 fi sleep 1 done ``` --- ## Migrate from Loki This guide explains how to migrate Loki log ingestion to GreptimeDB. GreptimeDB supports the Loki push API for log ingestion, so existing Loki-compatible writers can send logs to GreptimeDB with minimal configuration changes. The Loki-compatible endpoint in GreptimeDB is for ingestion. For querying and dashboards, use GreptimeDB SQL, [full-text search](/user-guide/logs/fulltext-search.md), the [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md), and the [Grafana integration](/user-guide/integrations/grafana.md) instead of LogQL. ## Before you start the migration Review the current Loki deployment and decide how logs should be stored in GreptimeDB: * Identify every writer that sends logs to Loki, such as Grafana Alloy, OpenTelemetry Collector, Promtail, Fluent Bit, Vector, or custom clients. * Plan the target GreptimeDB database and table name. If no table header is provided, GreptimeDB writes Loki logs to `loki_logs`. * Review Loki stream labels. GreptimeDB stores labels as tag columns, so avoid high-cardinality labels such as request IDs, user IDs, and trace IDs. * Decide whether raw log lines are enough or whether you need a GreptimeDB pipeline to parse log lines into structured columns. * Plan historical migration. GreptimeDB does not import Loki chunk or index files directly; migrate historical logs by replaying from the original log sources, archives, or exported records through GreptimeDB's log ingestion APIs. For new collector changes, [Grafana Alloy](/user-guide/integrations/alloy.md) is recommended. If you still run an existing Loki-compatible client, you can retarget it to GreptimeDB by changing the Loki push URL and adding GreptimeDB headers. ## Migration steps ### Configure the GreptimeDB Loki endpoint Send Loki push requests to: ```text http{s}://:4000/v1/loki/api/v1/push ``` Use the following GreptimeDB-specific headers: | Header | Required | Description | | --- | --- | --- | | `X-Greptime-DB-Name` | No | Target database name. The default is `public`. | | `X-Greptime-Log-Table-Name` | No | Target log table name. The default is `loki_logs`. | | `Authorization` | Depends on deployment | Basic authentication with Base64-encoded `:`. | | `X-Greptime-Pipeline-Name` or `X-Greptime-Log-Pipeline-Name` | No | Pipeline name for parsing Loki entries before insertion. | GreptimeDB accepts the same Loki push body shapes: * `Content-Type: application/x-protobuf`: a Snappy-compressed Loki `PushRequest`. * `Content-Type: application/json`: a JSON body with a top-level `streams` array. The following JSON request is useful for a quick connectivity check: ```bash curl -X POST "http://localhost:4000/v1/loki/api/v1/push" \ -H "Content-Type: application/json" \ -H "X-Greptime-DB-Name: public" \ -H "X-Greptime-Log-Table-Name: loki_demo_logs" \ --data-raw '{ "streams": [ { "stream": { "job": "api", "env": "prod" }, "values": [ ["1731748568804293888", "request completed", {"trace_id": "abc"}] ] } ] }' ``` ### Dual-write to Loki and GreptimeDB During migration, write to both Loki and GreptimeDB until you have validated ingestion, retention, dashboards, and alerts. The following Alloy example keeps the existing Loki sink and adds GreptimeDB as a second Loki-compatible sink: ```hcl loki.source.file "app" { targets = [ {__path__ = "/var/log/app/*.log"}, ] forward_to = [loki.process.app.receiver] file_match { enabled = true } } loki.process "app" { forward_to = [ loki.write.existing_loki.receiver, loki.write.greptimedb.receiver, ] stage.static_labels { values = { job = "app", env = "prod", } } } loki.write "existing_loki" { endpoint { url = "http://loki:3100/loki/api/v1/push" } } loki.write "greptimedb" { endpoint { url = "http://greptimedb:4000/v1/loki/api/v1/push" headers = { "X-Greptime-DB-Name" = "public", "X-Greptime-Log-Table-Name" = "loki_app_logs", } basic_auth { username = "" password = "" } } } ``` If your collector already has a Loki output, keep its labels and processing stages unchanged at first. Only change the GreptimeDB sink URL, database header, table header, and authentication settings. This example follows the Loki component pattern in the [Grafana Alloy guide](/user-guide/ingest-data/for-observability/alloy.md): `loki.source.file` reads files, `loki.process` keeps label processing in the Loki pipeline, and `loki.write.endpoint` carries the GreptimeDB URL, custom headers, and optional Basic authentication. Because the example uses a glob pattern in `__path__`, `file_match` enables Alloy's built-in file discovery so the pattern is expanded to matching files. If you use an exact file path, you can omit `file_match`. ### Validate the direct ingestion data model Without a pipeline, GreptimeDB stores Loki entries in a raw log table: | Loki data | GreptimeDB column | | --- | --- | | Entry timestamp | `greptime_timestamp` time index | | Log line | `line` field | | Structured metadata | `structured_metadata` JSON field | | Stream labels | String tag columns | For direct ingestion, let GreptimeDB create the table on the first write. Do not pre-create the direct-ingest table with SQL to specify label columns. Labels are dynamic and become tag columns in the generated schema. If you need a custom schema, use a pipeline and create the table from the pipeline configuration. Use SQL to verify the result: ```sql DESC loki_app_logs; SELECT greptime_timestamp, line, job, env, structured_metadata FROM loki_app_logs ORDER BY greptime_timestamp DESC LIMIT 10; ``` You can also check that the table is recognized as Loki log data: ```sql SELECT table_schema, table_name, signal_type, source FROM information_schema.table_semantics WHERE table_name = 'loki_app_logs'; ``` You can also open the [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) at `http://:4000/dashboard` and use Log View to query the ingested logs. ### Parse Loki log lines with a pipeline Use a GreptimeDB pipeline when the Loki log line contains JSON, logfmt, Nginx access logs, or another structured format that should become queryable columns. If you use an AI coding agent to create the pipeline, you can give it the [`greptimedb-pipeline` Skill](/faq-and-others/vibecoding.md#greptimedb-skills) so it can generate, dry-run, and refine the pipeline configuration. When `X-Greptime-Pipeline-Name` or `X-Greptime-Log-Pipeline-Name` is present, GreptimeDB sends each Loki entry through the pipeline with these input fields: | Pipeline input field | Description | | --- | --- | | `greptime_timestamp` | Loki entry timestamp. | | `loki_line` | Original Loki log line. | | `loki_label_` | Loki stream label value. | | `loki_metadata_` | Loki structured metadata value. | For example, suppose Alloy reads the following ZooKeeper log file: ```text 2015-08-25 11:23:58,959 - WARN [LearnerHandler-/10.10.34.11:45441:Leader@574] - Committing zxid 0xf00000000 from /10.10.34.13:2888 not first! 2015-08-25 11:23:58,960 - WARN [LearnerHandler-/10.10.34.11:45441:Leader@576] - First is 0x0 2015-08-25 11:23:58,960 - INFO [LearnerHandler-/10.10.34.11:45441:Leader@598] - Have quorum of supporters; starting up and setting last processed zxid: 0xf00000000 2015-08-25 11:26:27,891 - INFO [/10.10.34.13:3888:QuorumCnxManager$Listener@493] - Received connection request /10.10.34.12:57513 2015-08-25 11:26:27,897 - INFO [WorkerReceiver[myid=3]:FastLeaderElection@542] - Notification: 2 (n.leader), 0xd0000001b (n.zxid), 0x1 (n.round), LOOKING (n.state), 2 (n.sid), 0xd (n.peerEPoch), LEADING (my state) 2015-08-25 11:26:27,898 - INFO [WorkerReceiver[myid=3]:FastLeaderElection@542] - Notification: 3 (n.leader), 0xd0000001b (n.zxid), 0x3 (n.round), LOOKING (n.state), 2 (n.sid), 0xe (n.peerEPoch), LEADING (my state) 2015-08-25 11:26:28,138 - INFO [LearnerHandler-/10.10.34.12:38330:LearnerHandler@263] - Follower sid: 2 : info : org.apache.zookeeper.server.quorum.QuorumPeer$QuorumServer@7761c32f 2015-08-25 11:26:28,159 - INFO [LearnerHandler-/10.10.34.12:38330:LearnerHandler@318] - Synchronizing with Follower sid: 2 maxCommittedLog=0x0 minCommittedLog=0x0 peerLastZxid=0xd0000001b 2015-08-25 11:26:28,159 - INFO [LearnerHandler-/10.10.34.12:38330:LearnerHandler@395] - Sending SNAP 2015-08-25 11:26:28,159 - INFO [LearnerHandler-/10.10.34.12:38330:LearnerHandler@419] - Sending snapshot last zxid of peer is 0xd0000001b zxid of leader is 0xf00000000sent zxid of db as 0xf00000000 ``` Create a pipeline that extracts the ZooKeeper timestamp, level, thread, source line, and message: ```yaml # zk_pipeline.yaml version: 2 processors: - regex: fields: - loki_line, zk patterns: - '^(?\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) - (?[A-Z]+)\s+\[(?.*)@(?\d+)\] - (?.*)$' ignore_missing: true - date: fields: - zk_timestamp formats: - "%Y-%m-%d %H:%M:%S,%3f" timezone: "UTC" - select: fields: - zk_timestamp - zk_level - zk_thread - zk_source_line - zk_message - loki_label_job - loki_label_env transform: - field: zk_timestamp type: time index: timestamp - fields: - zk_level - loki_label_job - loki_label_env type: string tag: true - field: zk_thread type: string index: inverted - field: zk_source_line type: int32 - field: zk_message type: string index: fulltext ``` The sample timestamps do not include a time zone. Set `timezone` to the time zone used by the log producer. Upload the pipeline: ```bash curl -X POST "http://localhost:4000/v1/pipelines/zk_logs" \ -F "file=@zk_pipeline.yaml" ``` Then add the pipeline header to the GreptimeDB Loki sink: ```hcl loki.write "greptimedb" { endpoint { url = "http://greptimedb:4000/v1/loki/api/v1/push" headers = { "X-Greptime-DB-Name" = "public", "X-Greptime-Log-Table-Name" = "loki_zookeeper_logs", "X-Greptime-Pipeline-Name" = "zk_logs", } } } ``` When Alloy sends the ZooKeeper log file through this sink, GreptimeDB applies the pipeline before inserting rows into `loki_zookeeper_logs`. After writing through the pipeline, query the structured columns: ```sql SELECT zk_timestamp, loki_label_job AS job, loki_label_env AS env, zk_level, zk_thread, zk_source_line, zk_message FROM loki_zookeeper_logs WHERE zk_level = 'WARN' ORDER BY zk_timestamp DESC LIMIT 10; ``` For full-text search on parsed message fields, see [Full-Text Search](/user-guide/logs/fulltext-search.md). ### Migrate historical logs For a full historical migration, replay data into GreptimeDB rather than copying Loki storage files. Common approaches include: * Replay from the original log files or object-storage archives with Alloy, Vector, Fluent Bit, or custom scripts. * Export selected Loki query results into newline-delimited records, convert them to Loki push JSON, and write them to GreptimeDB. * Backfill only the retention window that must remain searchable after cutover, then rely on dual-write for new data. During backfill, keep the original timestamps in nanoseconds so GreptimeDB preserves event time. Run imports in bounded time ranges to make validation and retries manageable. ### Cut over reads and writes Before disabling Loki writes, validate: * Recent logs are present in GreptimeDB for every important service, namespace, and environment. * Row counts or sampled records match Loki for the same time windows. * GreptimeDB retention settings match the required log retention period. * Dashboards and alerts have been moved from LogQL to SQL or GreptimeDB log queries. * Full-text or skipping indexes exist for the columns used by frequent searches. After validation, remove the Loki sink from the collector configuration and keep the GreptimeDB sink as the only log destination. ## Troubleshooting ### Unsupported content type Set `Content-Type` to `application/x-protobuf` for Loki protobuf push clients or `application/json` for JSON requests. ### Protobuf decode or Snappy errors Loki protobuf push bodies must be Snappy-compressed. Do not send raw protobuf bytes without Snappy compression. ### Missing labels in GreptimeDB Check the collector processing stages before `loki.write`. Only labels that remain on the Loki stream when the request is sent become GreptimeDB tag columns. ### Table schema mismatch For direct Loki ingestion, let GreptimeDB auto-create the table. If you need a custom schema, use a pipeline and generate or create the table from the pipeline configuration. ## Related documentation * [Loki protocol](/user-guide/ingest-data/for-observability/loki.md) * [Grafana Alloy](/user-guide/ingest-data/for-observability/alloy.md) * [Manage Pipelines](/user-guide/logs/manage-pipelines.md) * [Full-Text Search](/user-guide/logs/fulltext-search.md) --- ## Migrate from MySQL This document will guide you through the migration process from MySQL to GreptimeDB. ## Before you start the migration Please be aware that though GreptimeDB supports the wire protocol of MySQL, it does not mean GreptimeDB implements all MySQL's features. You may refer to: * The [ANSI Compatibility](/reference/sql/compatibility.md) to see the constraints regarding using SQL in GreptimeDB. * The [Data Modeling Guide](/user-guide/deployments-administration/performance-tuning/design-table.md) to create proper table schemas. * The [Data Index Guide](/user-guide/manage-data/data-index.md) for index planning. ## Migration steps ### Create the databases and tables in GreptimeDB Before migrating the data from MySQL, you first need to create the corresponding databases and tables in GreptimeDB. GreptimeDB has its own SQL syntax for creating tables, so you cannot directly reuse the table creation SQLs that are produced by MySQL. When you write the table creation SQL for GreptimeDB, it's important to understand its "[data model](/user-guide/concepts/data-model.md)" first. Then, please take the following considerations in your create table SQL: 1. Since the time index column cannot be changed after the table is created, you need to choose the time index column carefully. The time index is best set to the natural timestamp when the data is generated, as it provides the most intuitive way to query the data, and the best query performance. For example, in the IOT scenes, you can use the collection time of sensor data as the time index; or the occurrence time of an event in the observability scenes. 2. In this migration process, it's not recommend to create another synthetic timestamp, such as a new column created with `DEFAULT current_timestamp()` as the time index column. It's not recommend to use the random timestamp as the time index either. 3. It's vital to set the most fit timestamp precision for your time index column, too. Like the chosen of time index column, the precision of it cannot be changed as well. Find the most fit timestamp type for your data set [here](/reference/sql/data-types.md#data-types-compatible-with-mysql-and-postgresql). 4. Choose a primary key only when it is truly needed. The primary key in GreptimeDB is different from that in MySQL. You should use a primary key only when: * Most queries can benefit from the ordering. * You need to deduplicate (including delete) rows by the primary key and time index. Otherwise, setting a primary key is optional and it may hurt performance. Read [Primary Key](/user-guide/deployments-administration/performance-tuning/design-table.md#primary-key) for details. Finally please refer to "[CREATE](/reference/sql/create.md)" SQL document for more details for choosing the right data types and "ttl" or "compaction" options, etc. 5. Choose proper indexes to speed up queries. * Inverted index: is ideal for filtering by low-cardinality columns and quickly finding rows with specific values. * Skipping index: works well with sparse data. * Fulltext index: enables efficient keyword and pattern search in large text columns. For details and best practices, refer to the [data index](user-guide/manage-data/data-index.md) documentation. ### Write data to both GreptimeDB and MySQL simultaneously Writing to both GreptimeDB and MySQL keeps the two systems in parallel so you can compare them before cutting over. It is not a transaction across both databases: a write can succeed on one side and fail on the other, so record failed writes, retry each destination independently, and reconcile the two before switching over. Check that a row is actually missing before replaying it — an [append-only table](/user-guide/manage-data/overview.md#avoid-updating-data-by-creating-table-with-append_mode-option) keeps duplicate rows rather than merging them. By utilizing MySQL's client libraries (JDBC + a MySQL driver), you can set up two client instances - one for GreptimeDB and another for MySQL. For guidance on writing data to GreptimeDB using SQL, please refer to the [Ingest Data](/user-guide/ingest-data/for-iot/sql.md) section. If retaining all historical data isn't necessary, you can simultaneously write data to both GreptimeDB and MySQL for a specific period to accumulate the required recent data. Subsequently, cease writing to MySQL and continue exclusively with GreptimeDB. If a complete migration of all historical data is needed, please proceed with the following steps. ### Export data from MySQL The command below exports the tables in full, including rows that dual-write has already delivered to GreptimeDB. Use it as-is only when the destination table deduplicates on its primary key and time index. Otherwise, import it into a staging table and copy across only the rows below the cutoff, or add `--where` on a time column to the export — note that `--where` applies to every table in the dump, so tables that do not share that column have to be exported separately. [mysqldump](https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html) is a commonly used tool to export data from MySQL. Using it, we can export the data that can be later imported into GreptimeDB directly. For example, if we want to export two databases, `db1` and `db2` from MySQL, we can use the following command: ```bash mysqldump -h127.0.0.1 -P3306 -umysql_user -p --compact -cnt --skip-extended-insert --databases db1 db2 > /path/to/output.sql ``` Replace the `-h`, `-P` and `-u` flags with the appropriate values for your MySQL server. The `--databases` flag is used to specify the databases to be exported. The output will be written to the `/path/to/output.sql` file. The content in the `/path/to/output.sql` file should be like this: ```plaintext ~ ❯ cat /path/to/output.sql USE `db1`; INSERT INTO `foo` (`ts`, `a`, `b`) VALUES (1,'hello',1); INSERT INTO ... USE `db2`; INSERT INTO `foo` (`ts`, `a`, `b`) VALUES (2,'greptime',2); INSERT INTO ... ``` ### Import data into GreptimeDB The [MySQL Command-Line Client](https://dev.mysql.com/doc/refman/8.4/en/mysql.html) can be used to import data into GreptimeDB. Continuing the above example, say the data is exported to file `/path/to/output.sql`, then we can use the following command to import the data into GreptimeDB: ```bash mysql -h127.0.0.1 -P4002 -ugreptime_user -p -e "source /path/to/output.sql" ``` Replace the `-h`, `-P` and `-u` flags with the appropriate values for your GreptimeDB server. The `source` command is used to execute the SQL commands in the `/path/to/output.sql` file. Add `-vvv` to see the detailed execution results for debugging purpose. To summarize, data migration steps can be illustrate as follows: ![migrate mysql data steps](/migration-mysql.jpg) After the data migration is completed, you can stop writing data to MySQL and continue using GreptimeDB exclusively! If you need a more detailed migration plan or example scripts, please provide the specific table structure and data volume. The [GreptimeDB official community](https://github.com/orgs/GreptimeTeam/discussions) will offer further support. Welcome to join the [Greptime Slack](http://greptime.com/slack). --- ## Migrate from PostgreSQL This document will guide you through the migration process from PostgreSQL to GreptimeDB. ## Before you start the migration Please be aware that though GreptimeDB supports the wire protocol of PostgreSQL, it does not mean GreptimeDB implements all PostgreSQL's features. You may refer to: * The [ANSI Compatibility](/reference/sql/compatibility.md) to see the constraints regarding using SQL in GreptimeDB. * The [Data Modeling Guide](/user-guide/deployments-administration/performance-tuning/design-table.md) to create proper table schemas. * The [Data Index Guide](/user-guide/manage-data/data-index.md) for index planning. ## Migration steps ### Create the databases and tables in GreptimeDB Before migrating the data from PostgreSQL, you first need to create the corresponding databases and tables in GreptimeDB. GreptimeDB has its own SQL syntax for creating tables, so you cannot directly reuse the table creation SQLs that are produced by PostgreSQL. When you write the table creation SQL for GreptimeDB, it's important to understand its "[data model](/user-guide/concepts/data-model.md)" first. Then, please take the following considerations in your create table SQL: 1. Since the time index column cannot be changed after the table is created, you need to choose the time index column carefully. The time index is best set to the natural timestamp when the data is generated, as it provides the most intuitive way to query the data, and the best query performance. For example, in the IOT scenes, you can use the collection time of sensor data as the time index; or the occurrence time of an event in the observability scenes. 2. In this migration process, it's not recommend to create another synthetic timestamp, such as a new column created with `DEFAULT current_timestamp()` as the time index column. It's not recommend to use the random timestamp as the time index either. 3. It's vital to set the most fit timestamp precision for your time index column, too. Like the chosen of time index column, the precision of it cannot be changed as well. Find the most fit timestamp type for your data set [here](/reference/sql/data-types.md#data-types-compatible-with-mysql-and-postgresql). 4. Choose a primary key only when it is truly needed. The primary key in GreptimeDB is different from that in PostgreSQL. You should use a primary key only when: * Most queries can benefit from the ordering. * You need to deduplicate (including delete) rows by the primary key and time index. Otherwise, setting a primary key is optional and it may hurt performance. Read [Primary Key](/user-guide/deployments-administration/performance-tuning/design-table.md#primary-key) for details. Finally please refer to "[CREATE](/reference/sql/create.md)" SQL document for more details for choosing the right data types and "ttl" or "compaction" options, etc. 5. Choose proper indexes to speed up queries. * Inverted index: is ideal for filtering by low-cardinality columns and quickly finding rows with specific values. * Skipping index: works well with sparse data. * Fulltext index: enables efficient keyword and pattern search in large text columns. For details and best practices, refer to the [data index](user-guide/manage-data/data-index.md) documentation. ### Write data to both GreptimeDB and PostgreSQL simultaneously Writing to both GreptimeDB and PostgreSQL keeps the two systems in parallel so you can compare them before cutting over. It is not a transaction across both databases: a write can succeed on one side and fail on the other, so record failed writes, retry each destination independently, and reconcile the two before switching over. Check that a row is actually missing before replaying it — an [append-only table](/user-guide/manage-data/overview.md#avoid-updating-data-by-creating-table-with-append_mode-option) keeps duplicate rows rather than merging them. By utilizing PostgreSQL's client libraries (JDBC + a PostgreSQL driver), you can set up two client instances - one for GreptimeDB and another for PostgreSQL. For guidance on writing data to GreptimeDB using SQL, please refer to the [Ingest Data](/user-guide/ingest-data/for-iot/sql.md) section. If retaining all historical data isn't necessary, you can simultaneously write data to both GreptimeDB and PostgreSQL for a specific period to accumulate the required recent data. Subsequently, cease writing to PostgreSQL and continue exclusively with GreptimeDB. If a complete migration of all historical data is needed, please proceed with the following steps. ### Export data from PostgreSQL `pg_dump` takes a consistent snapshot, but that snapshot still contains the rows dual-write has already delivered to GreptimeDB. The command below exports them too. Use it as-is only when the destination table deduplicates on its primary key and time index. Otherwise export the historical window explicitly, for example with `COPY (SELECT ... WHERE ts < '') TO ...` per table. [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html) is a commonly used tool to export data from PostgreSQL. Using it, we can export the data that can be later imported into GreptimeDB directly. For example, if we want to export schemas whose names start with `db` in the database `postgres` from PostgreSQL, we can use the following command: ```bash pg_dump -h127.0.0.1 -p5432 -Upostgres -ax --column-inserts --no-comments -n 'db*' postgres | grep -v "^SE" > /path/to/output.sql ``` Replace the `-h`, `-p` and `-U` flags with the appropriate values for your PostgreSQL server. The `-n` flag is used to specify the schemas to be exported. And the database `postgres` is at the end of the `pg_dump` command line. Note that we pipe the `pg_dump` output through a special `grep`, to remove some unnecessary `SET` or `SELECT` lines. The final output will be written to the `/path/to/output.sql` file. The content in the `/path/to/output.sql` file should be like this: ```plaintext ~ ❯ cat /path/to/output.sql -- -- PostgreSQL database dump -- -- Dumped from database version 16.4 (Debian 16.4-1.pgdg120+1) -- Dumped by pg_dump version 16.4 -- -- Data for Name: foo; Type: TABLE DATA; Schema: db1; Owner: postgres -- INSERT INTO db1.foo (ts, a) VALUES ('2024-10-31 00:00:00', 1); INSERT INTO db1.foo (ts, a) VALUES ('2024-10-31 00:00:01', 2); INSERT INTO db1.foo (ts, a) VALUES ('2024-10-31 00:00:01', 3); INSERT INTO ... -- -- Data for Name: foo; Type: TABLE DATA; Schema: db2; Owner: postgres -- INSERT INTO db2.foo (ts, b) VALUES ('2024-10-31 00:00:00', '1'); INSERT INTO db2.foo (ts, b) VALUES ('2024-10-31 00:00:01', '2'); INSERT INTO db2.foo (ts, b) VALUES ('2024-10-31 00:00:01', '3'); INSERT INTO ... -- -- PostgreSQL database dump complete -- ``` ### Import data into GreptimeDB The [psql -- PostgreSQL interactive terminal](https://www.postgresql.org/docs/current/app-psql.html) can be used to we can use the following command to import the data into GreptimeDB: ```bash psql -h127.0.0.1 -p4003 -d public -f /path/to/output.sql ``` Replace the `-h` and `-p` flags with the appropriate values for your GreptimeDB server. The `-d` of the psql command is used to specify the database and cannot be omitted. The value `public` of `-d` is the default database used by GreptimeDB. You can also add `-a` to the end to see every executed line, or `-s` for entering the single-step mode. To summarize, data migration steps can be illustrate as follows: ![migrate postgresql data steps](/migration-postgresql.jpg) After the data migration is completed, you can stop writing data to PostgreSQL and continue using GreptimeDB exclusively! If you need a more detailed migration plan or example scripts, please provide the specific table structure and data volume. The [GreptimeDB official community](https://github.com/orgs/GreptimeTeam/discussions) will offer further support. Welcome to join the [Greptime Slack](http://greptime.com/slack). --- ## Migrate from Prometheus For information on configuring Prometheus to write data to GreptimeDB, please refer to the [remote write](/user-guide/ingest-data/for-observability/prometheus.md#remote-write-configuration) documentation. For detailed information on querying data in GreptimeDB using Prometheus query language, please refer to the [HTTP API](/user-guide/query-data/promql.md#prometheus-http-api) section in the PromQL documentation. To add GreptimeDB as a Prometheus data source in Grafana, please refer to the [Grafana](/user-guide/integrations/grafana.md#prometheus-data-source) documentation. --- ## Migrate to GreptimeDB You can migrate data from various databases to GreptimeDB, including InfluxDB, MySQL, PostgreSQL, Prometheus, Loki, and more. --- ## User Guide This guide covers running GreptimeDB: getting data in, querying it, processing it, and operating a deployment. For the data model and architecture behind these tasks, see [Concepts](./concepts/overview.md). To install GreptimeDB and run a first query, start with the [Quick Start](/getting-started/quick-start.md). ## Ingest Data [Ingest Data](./ingest-data/overview.md) maps each data source to the protocol it writes with and the guide that covers it. On most paths, tables and columns are created as data arrives; [SQL, Flink, and Spark](./ingest-data/overview.md#automatic-schema-generation) write into an existing table. Logs and traces have their own guides, covering storage and querying beyond the write protocol: - [Logs](./logs/overview.md) — parse and transform text logs with pipelines before they are stored. - [Traces](./traces/overview.md) — store OTLP traces and query them with SQL or the Jaeger-compatible API. ## Query Data - [SQL](./query-data/sql.md) — queries across metrics, logs, and traces, including range queries, CTEs, joins, and views. - [PromQL](./query-data/promql.md) — metric queries through the Prometheus HTTP API, or `TQL` inside SQL. - [Jaeger API](./query-data/jaeger.md) — trace queries from Jaeger UI or Grafana. The [query overview](./query-data/overview.md) compares the interfaces. ## Process and Describe Data - [Flow](./flow-computation/overview.md) — continuous aggregation from incoming rows into sink tables. - [Data Index](./manage-data/data-index.md) — inverted, skipping, and full-text indexes, and when each one pays off. - [Update, Delete, and TTL](./manage-data/overview.md) — updating by overwrite, deleting, and expiring data. - [Table Semantic Layer](./concepts/semantic-layer.md) — `greptime.semantic.*` metadata recording the signal, source, and unit behind each table. - [Vector Storage](./vectors/vector-type.md) — the vector data type and similarity search. ## Deploy and Operate - [Deployments & Administration](./deployments-administration/overview.md) — Kubernetes and bare-metal deployment, capacity planning, WAL modes, monitoring, disaster recovery, and Region operations. - [Migrate to GreptimeDB](./migrate-to-greptimedb/overview.md) — protocol, query, dashboard, and historical-data changes when moving from Prometheus, InfluxDB, Loki, Elasticsearch, or MySQL. - [Time Zone](./timezone.md) — how the session time zone affects ingestion and query results. ## Connect Other Tools - [Protocols](./protocols/overview.md) — the wire protocols GreptimeDB speaks, and the boundaries of each one. - [Integrations](./integrations/overview.md) — Grafana, vmalert, Superset, Metabase, Flink, Spark, the MCP Server, and other tools that connect to GreptimeDB. --- ## Elasticsearch(Protocols) GreptimeDB implements the Elasticsearch [`_bulk` API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html) for ingestion, so collectors that already speak it — Logstash, Filebeat, Telegraf — write to GreptimeDB by changing the endpoint. Two endpoints are available, both `POST` with an NDJSON body: | Endpoint | Purpose | | --- | --- | | `/v1/elasticsearch/_bulk` | The index comes from each command line's `_index` field | | `/v1/elasticsearch/${index}/_bulk` | The path supplies the index, but an `_index` field in the body overrides it | What the implementation covers: - An Elasticsearch index maps to a GreptimeDB table. The `db` URL parameter selects the database. - Both `index` and `create` commands are treated as inserts. Modification and deletion are not supported. - Only the `_index` field is read from a command line; the other fields are ignored. - Writes only. Querying uses [SQL](/user-guide/query-data/sql.md), not the Elasticsearch Query DSL. Collector configuration and worked examples are in [Ingest Data with Elasticsearch](/user-guide/ingest-data/for-observability/elasticsearch.md). --- ## gRPC GreptimeDB offers [gRPC SDKs](/user-guide/ingest-data/for-iot/grpc-sdks/overview.md) for efficient and high-performance data ingestion. If there is no SDK available for your programming language, you have the option to [create your own SDK](/contributor-guide/how-to/how-to-write-sdk.md) by following the guidelines provided in the contributor guide. --- ## HTTP API GreptimeDB provides HTTP APIs for interacting with the database. For a complete list of available APIs, please refer to the [HTTP Endpoint](/reference/http-endpoints.md). ## Base URL The base URL of the API is `http(s)://:/`. - For the GreptimeDB instance running on the local machine, with default port configuration `4000`, the base URL is `http://localhost:4000/`. You can change the server host and port in the [configuration](/user-guide/deployments-administration/configuration.md#protocol-options) file. - For GreptimeCloud, the base URL is `https:///`. You can find the host in 'Connection Information' at the GreptimeCloud console. In the following sections, we use `http:///` as the base URL to demonstrate the API usage. ## Common headers ### Authentication Assume that you have already configured the database [authentication](/user-guide/deployments-administration/authentication/overview.md) correctly, GreptimeDB supports the built-in `Basic` authentication scheme in the HTTP API. To set up authentication, follow these steps: 1. Encode your username and password using the `:` format and the `Base64` algorithm. 2. Attach your encoded credentials to the one of the following HTTP header: - `Authorization: Basic ` - `x-greptime-auth: Basic ` Here's an example. If you want to connect to GreptimeDB using the username `greptime_user` and password `greptime_pwd`, use the following command: ```shell curl -X POST \ -H 'Authorization: Basic Z3JlcHRpbWVfdXNlcjpncmVwdGltZV9wd2Q=' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=show tables' \ http://localhost:4000/v1/sql ``` In this example, `Z3JlcHRpbWVfdXNlcjpncmVwdGltZV9wd2Q=` represents the Base64-encoded value of `greptime_user:greptime_pwd`. Make sure to replace it with your own configured username and password, and encode them using Base64. :::tip NOTE InfluxDB uses its own authentication format, see [InfluxDB](./influxdb-line-protocol.md) for details. ::: ### Timeout GreptimeDB supports the `X-Greptime-Timeout` header in HTTP requests. It is used to specify the timeout for the request running in the database server. For example, the following request set `120s` timeout for the request: ```bash curl -X POST \ -H 'Authorization: Basic ' \ -H 'X-Greptime-Timeout: 120s' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=show tables' \ http://localhost:4000/v1/sql ``` ### Hints GreptimeDB supports the `x-greptime-hints` header in HTTP requests to pass hint key-value pairs that influence request behavior. These hints are primarily used to set table options when tables are [automatically created](/user-guide/ingest-data/overview.md#automatic-schema-generation) during data ingestion. The `x-greptime-hints` format is a comma-separated list of `key=value` pairs: ``` x-greptime-hints: key1=value1, key2=value2 ``` You can also pass one hint per header by using `x-greptime-hint-`: ``` x-greptime-hint-key1: value1 x-greptime-hint-key2: value2 ``` Supported hints: | Hint | Type | Default | Description | | --- | --- | --- | --- | | `auto_create_table` | Boolean | `true` | Whether to automatically create the table if it does not exist when inserting data. | | `ttl` | Duration string | None | Sets the [time-to-live](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies) for the table, e.g. `7d`, `24h`. Expired data will be automatically purged. | | `append_mode` | Boolean | `false` | Enables [append-only mode](/reference/sql/create.md#create-an-append-only-table) for the table, which disables deduplication by primary key and supports duplicate rows. For InfluxDB line protocol writes, an explicit `append_mode=true` hint creates the table with `append_mode = 'true'` and `merge_mode = 'last_row'`. | | `merge_mode` | String | None | Sets the [merge mode](/reference/sql/create.md#create-a-table-with-merge-mode) for the table, e.g. `last_non_null`, `last_row`. For auto-created InfluxDB line protocol tables, this hint takes precedence over [`influxdb.default_merge_mode`](/user-guide/deployments-administration/configuration.md), which defaults to `last_non_null`. When `append_mode` is enabled, only `last_row` is allowed. | | `physical_table` | String | None | Specifies the physical table name for the [metric engine](/contributor-guide/datanode/metric-engine.md). | | `query.enable_remote_dynamic_filter_pushdown` | Boolean | `true` | Enables remote dynamic filter pushdown for SQL queries. Set it to `false` to disable Frontend-to-Datanode dynamic filter propagation for the current request. See [Remote dynamic filter pushdown](/user-guide/query-data/sql.md#remote-dynamic-filter-pushdown). | | `skip_wal` | Boolean | `false` | Skips WAL (Write-Ahead Log) writes for the table. | | `sst_format` | String | None | Sets the SST (Sorted String Table) file format for the table. Valid values: `flat`, `primary_key`. | | `trace_table_partitions` | Int | None | Override default partition number (16) of trace tables. Set to `0` or `1` to disable partitioning. | For example, the following request sets TTL and append mode for auto-created tables: ```bash curl -X POST \ -H 'Authorization: Basic ' \ -H 'x-greptime-hints: ttl=7d, append_mode=true' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=INSERT INTO my_table VALUES (...)' \ http://localhost:4000/v1/sql ``` ## Admin APIs :::tip NOTE These endpoint cannot be used in GreptimeCloud. ::: Please refer to the [Admin APIs](/reference/http-endpoints.md#admin-apis) documentation for more information. ## POST SQL statements To submit a SQL query to the GreptimeDB server via HTTP API, use the following format: ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'X-Greptime-Timeout: ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=' \ 'http:///v1/sql' ``` ### Headers - [`Authorization`](#authentication) - [`X-Greptime-Timeout`](#timeout) - `Content-Type`: `application/x-www-form-urlencoded`. - `X-Greptime-Timezone`: The time zone for the current SQL query. Optional. Please refer to [time zone](#time-zone). ### Query string parameters - `db`: The database name. Optional. If not provided, the default database `public` will be used. - `format`: The output format. Optional. `greptimedb_v1` by default. In addition to the default JSON format, the HTTP API also allows you to customize output format by providing the `format` query parameter with following values: - `influxdb_v1`: [influxdb query API](https://docs.influxdata.com/influxdb/v1/tools/api/#query-http-endpoint) compatible format. Additional parameters: - `epoch`: `[ns,u,µ,ms,s,m,h]`, returns epoch timestamps with the specified precision - `csv`: outputs as comma-separated values - `csvWithNames`: outputs as comma-separated values with a column names header - `csvWithNamesAndTypes`: outputs as comma-separated values with column names and data types headers - `arrow`: [Arrow IPC format](https://arrow.apache.org/docs/python/feather.html). We use stream format for this API. Additional parameters: - `compression`: `zstd` or `lz4`, default: no compression - `table`: ASCII table format for console output - `null`: Concise text-only output of the row count and elapsed time, useful for evaluating query performance. ### Body - `sql`: The SQL statement. Required. ### Response The response is a JSON object containing the following fields: - `output`: The SQL executed result. Please refer to the examples below to see the details. - `execution_time_ms`: The execution time of the statement in milliseconds. ### Examples #### `INSERT` statement For example, to insert data into the `monitor` table of database `public`, use the following command: ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=INSERT INTO monitor VALUES ("127.0.0.1", 1667446797450, 0.1, 0.4), ("127.0.0.2", 1667446798450, 0.2, 0.3), ("127.0.0.1", 1667446798450, 0.5, 0.2)' \ http://localhost:4000/v1/sql?db=public ``` The response will contain the number of affected rows: ```shell {"output":[{"affectedrows":3}],"execution_time_ms":11} ``` #### `SELECT` statement You can also use the HTTP API to execute other SQL statements. For example, to retrieve data from the `monitor` table: ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ http://localhost:4000/v1/sql?db=public ``` The response will contain the queried data in JSON format: ```json { "output": [ { "records": { "schema": { "column_schemas": [ { "name": "host", "data_type": "String" }, { "name": "ts", "data_type": "TimestampMillisecond" }, { "name": "cpu", "data_type": "Float64" }, { "name": "memory", "data_type": "Float64" } ] }, "rows": [ [ "127.0.0.1", 1720728000000, 0.5, 0.1 ] ], "total_rows": 1 } } ], "execution_time_ms": 7 } ``` The response contains the following fields: - `output`: The execution result. - `records`: The query result. - `schema`: The schema of the result, including the schema of each column. - `rows`: The rows of the query result, where each row is an array containing the corresponding values of the columns in the schema. - `execution_time_ms`: The execution time of the statement in milliseconds. #### Time zone GreptimeDB supports the `X-Greptime-Timezone` header in HTTP requests. It is used to specify the timezone for the current SQL query. For example, the following request uses the time zone `+1:00` for the query: ```bash curl -X POST \ -H 'Authorization: Basic ' \ -H 'X-Greptime-Timezone: +1:00' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=SHOW VARIABLES time_zone;' \ http://localhost:4000/v1/sql ``` After the query, the result will be: ```json { "output": [ { "records": { "schema": { "column_schemas": [ { "name": "TIME_ZONE", "data_type": "String" } ] }, "rows": [ [ "+01:00" ] ] } } ], "execution_time_ms": 27 } ``` For information on how the time zone affects data inserts and queries, please refer to the SQL documents in the [Ingest Data](../ingest-data/for-iot/sql.md#time-zone) and [Query Data](../query-data/sql.md#time-zone) sections. #### Query data with `table` format output You can use the `table` format in the query string parameters to get the output in ASCII table format. ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ 'http://localhost:4000/v1/sql?db=public&format=table' ``` Output ``` ┌─host────────┬─ts────────────┬─cpu─┬─memory─┐ │ "127.0.0.1" │ 1667446797450 │ 0.1 │ 0.4 │ │ "127.0.0.1" │ 1667446798450 │ 0.5 │ 0.2 │ │ "127.0.0.2" │ 1667446798450 │ 0.2 │ 0.3 │ └─────────────┴───────────────┴─────┴────────┘ ``` #### Query data with `csvWithNames` format output ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ 'http://localhost:4000/v1/sql?db=public&format=csvWithNames' ``` Output: ```csv host,ts,cpu,memory 127.0.0.1,1667446797450,0.1,0.4 127.0.0.1,1667446798450,0.5,0.2 127.0.0.2,1667446798450,0.2,0.3 ``` Changes `format` to `csvWithNamesAndTypes`: ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ 'http://localhost:4000/v1/sql?db=public&format=csvWithNamesAndTypes' ``` Output: ```csv host,ts,cpu,memory String,TimestampMillisecond,Float64,Float64 127.0.0.1,1667446797450,0.1,0.4 127.0.0.1,1667446798450,0.5,0.2 127.0.0.2,1667446798450,0.2,0.3 ``` #### Query data with `influxdb_v1` format output You can use the `influxdb_v1` format in the query string parameters to get the output in InfluxDB query API compatible format. ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ 'http://localhost:4000/v1/sql?db=public&format=influxdb_v1&epoch=ms' ``` ```json { "results": [ { "statement_id": 0, "series": [ { "name": "", "columns": [ "host", "ts", "cpu", "memory" ], "values": [ ["127.0.0.1", 1667446797450, 0.1, 0.4], ["127.0.0.1", 1667446798450, 0.5, 0.2], ["127.0.0.2", 1667446798450, 0.2, 0.3] ] } ] } ], "execution_time_ms": 2 } ``` ### Format SQL with GreptimeDB's SQL dialect To rewrite an SQL statement into the canonical form of GreptimeDB's SQL dialect, use the `/v1/sql/format` endpoint. The endpoint parses the input with GreptimeDB's parser and serializes the statement back to text, which normalizes keyword casing and spacing and terminates the statement with a semicolon. It does not re-indent or wrap the statement: every statement is returned on a single line. This is useful for normalizing SQL before storing or comparing it. Available since v0.17. ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=select 1 as x" \ http://localhost:4000/v1/sql/format ``` The response contains the formatted SQL string: ```json { "formatted": "SELECT 1 AS x;" } ``` You can also pass the SQL as a query parameter with `GET`: ```shell curl -X GET \ -H 'Authorization: Basic ' \ 'http://localhost:4000/v1/sql/format?sql=select%201%20as%20x' ``` A `POST` request must set `Content-Type: application/x-www-form-urlencoded`, otherwise it is rejected before it reaches the endpoint. When the SQL is present in both the query string and the form body, the query parameter wins. If the input holds several statements, `formatted` contains all of them joined by newlines: ```json { "formatted": "SELECT 1 AS x;\nSELECT 2 AS y;" } ``` If the `sql` parameter is missing, the endpoint returns HTTP status `400` with error code `1004` and the message `sql parameter is required.`. If the SQL cannot be parsed, it returns the parser error. ### Parse SQL with GreptimeDB's SQL dialect To parse and understand queries written in GreptimeDB's SQL dialect for tools like dashboards, etc., you can use the `/v1/sql/parse` endpoint to obtain the structured result of an SQL query: ```shell curl -X POST \ -H 'Authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "sql=SELECT * FROM monitor" \ http://localhost:4000/v1/sql/parse ``` ```json [ { "Query": { "inner": { "with": null, "body": { "Select": { "select_token": { "token": { "Word": { "value": "SELECT", "quote_style": null, "keyword": "SELECT" } }, "span": { "start": { "line": 1, "column": 1 }, "end": { "line": 1, "column": 7 } } }, "optimizer_hint": null, "distinct": null, "select_modifiers": null, "top": null, "top_before_distinct": false, "projection": [ { "Wildcard": { "wildcard_token": { "token": "Mul", "span": { "start": { "line": 1, "column": 8 }, "end": { "line": 1, "column": 9 } } }, "opt_ilike": null, "opt_exclude": null, "opt_except": null, "opt_replace": null, "opt_rename": null } } ], "exclude": null, "into": null, "from": [ { "relation": { "Table": { "name": [ { "Identifier": { "value": "monitor", "quote_style": null, "span": { "start": { "line": 1, "column": 15 }, "end": { "line": 1, "column": 22 } } } } ], "alias": null, "args": null, "with_hints": [], "version": null, "with_ordinality": false, "partitions": [], "json_path": null, "sample": null, "index_hints": [] } }, "joins": [] } ], "lateral_views": [], "prewhere": null, "selection": null, "connect_by": [], "group_by": { "Expressions": [ [], [] ] }, "cluster_by": [], "distribute_by": [], "sort_by": [], "having": null, "named_window": [], "qualify": null, "window_before_qualify": false, "value_table_mode": null, "flavor": "Standard" } }, "order_by": null, "limit_clause": null, "fetch": null, "locks": [], "for_clause": null, "settings": null, "format_clause": null, "pipe_operators": [] }, "hybrid_cte": null } } ] ``` ## POST PromQL queries ### The API returns Prometheus query result format GreptimeDB compatible with Prometheus query language (PromQL) and can be used to query data in GreptimeDB. For all the compatible APIs, please refer to the [Prometheus Query Language](/user-guide/query-data/promql#prometheus-http-api) documentation. ### The API returns GreptimeDB JSON format GreptimeDB also exposes an custom HTTP API for querying with PromQL, and returning GreptimeDB's data frame output. You can find it on `/promql` path under the current stable API version `/v1`. The return value of this API is in GreptimeDB's JSON format, not Prometheus's format. For example: ```shell curl -X GET \ -H 'Authorization: Basic ' \ -G \ --data-urlencode 'query=avg(system_metrics{idc="idc_a"})' \ --data-urlencode 'start=1667446797' \ --data-urlencode 'end=1667446799' \ --data-urlencode 'step=1s' \ 'http://localhost:4000/v1/promql?db=public' ``` The input parameters are similar to the [`range_query`](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in Prometheus' HTTP API: - `db=`: Required when using GreptimeDB with authorization, otherwise can be omitted if you are using the default `public` database. Note this parameter should be set in the query param, or using a HTTP header `--header 'x-greptime-db-name: '`. - `query=`: Required. Prometheus expression query string. - `start=`: Required. The start timestamp, which is inclusive. It is used to set the range of time in `TIME INDEX` column. - `end=`: Required. The end timestamp, which is inclusive. It is used to set the range of time in `TIME INDEX` column. - `step=`: Required. Query resolution step width in duration format or float number of seconds. - `format`: The output format. Optional. `greptimedb_v1` by default. In addition to the default JSON format, the HTTP API also allows you to customize output format by providing the `format` query parameter with following values: - `influxdb_v1`: [influxdb query API](https://docs.influxdata.com/influxdb/v1/tools/api/#query-http-endpoint) compatible format. Additional parameters: - `epoch`: `[ns,u,µ,ms,s,m,h]`, returns epoch timestamps with the specified precision - `csv`: outputs as comma-separated values - `csvWithNames`: outputs as comma-separated values with a column names header - `csvWithNamesAndTypes`: outputs as comma-separated values with column names and data types headers - `arrow`: [Arrow IPC format](https://arrow.apache.org/docs/python/feather.html). Additional parameters: - `compression`: `zstd` or `lz4`, default: no compression - `table`: ASCII table format for console output - `null`: Concise text-only output of the row count and elapsed time, useful for evaluating query performance. Here are some examples for each type of parameter: - rfc3339 - `2015-07-01T20:11:00Z` (default to seconds resolution) - `2015-07-01T20:11:00.781Z` (with milliseconds resolution) - `2015-07-02T04:11:00+08:00` (with timezone offset) - unix timestamp - `1435781460` (default to seconds resolution) - `1435781460.781` (with milliseconds resolution) - duration - `1h` (1 hour) - `5d1m` (5 days and 1 minute) - `2` (2 seconds) - `2s` (also 2 seconds) The result format is the same as `/sql` interface described in [Post SQL statements](#post-sql-statements) section. ```json { "output": [ { "records": { "schema": { "column_schemas": [ { "name": "ts", "data_type": "TimestampMillisecond" }, { "name": "AVG(system_metrics.cpu_util)", "data_type": "Float64" }, { "name": "AVG(system_metrics.memory_util)", "data_type": "Float64" }, { "name": "AVG(system_metrics.disk_util)", "data_type": "Float64" } ] }, "rows": [ [ 1667446798000, 80.1, 70.3, 90 ], [ 1667446799000, 80.1, 70.3, 90 ] ] } } ], "execution_time_ms": 5 } ``` ## Post Influxdb line protocol data ```shell curl -X POST \ -H 'Authorization: token ' \ -d '' \ 'http:///v1/influxdb/api/v2/write?bucket=&precision=' ``` ```shell curl -X POST \ -d '' \ 'http:///v1/influxdb/write?u=&p=&precision=' ``` ### Headers - `Authorization`: **Unlike other APIs**, the InfluxDB line protocol APIs use the InfluxDB authentication format. For V2, it is `token `. ### Query string parameters - `u`: The username. Optional. It is the authentication username for V1. - `p`: The password. Optional. It is the authentication password for V1. - `bucket`: The database name for InfluxDB line protocol V2 writes. Optional. The default value is `public`. - `db`: The database name. This is the standard parameter for InfluxDB line protocol V1 writes. The V2 API also accepts `db` as a GreptimeDB compatibility alias, but `bucket` is recommended for V2. Optional. The default value is `public`. - `precision`: Defines the precision of the timestamp provided in the request body. Please refer to [InfluxDB Line Protocol](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md) in the user guide. ### Body The InfluxDB line protocol data points. Please refer to the [InfluxDB Line Protocol](https://docs.influxdata.com/influxdb/v1/write_protocols/line_protocol_tutorial/#syntax) document. ### Examples Please refer to [InfluxDB Line Protocol](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md) in the user guide. ## API for managing Pipelines When writing logs to GreptimeDB, you can use HTTP APIs to manage the pipelines. For detailed information, please refer to the [Manage Pipelines](/user-guide/logs/manage-pipelines.md) documentation. --- ## InfluxDB Line Protocol(Protocols) ## Ingest data For how to ingest data to GreptimeDB using InfluxDB Line Protocol, please refer to the [Ingest Data](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md) document. ## HTTP API Please refer to the [HTTP API](http.md#post-influxdb-line-protocol-data) document for details on the InfluxDB Line Protocol API. ## PING Additionally, GreptimeDB also provides support for the `ping` and `health` APIs of InfluxDB. Use `curl` to request `ping` API. ```shell curl -i "127.0.0.1:4000/v1/influxdb/ping" ``` ```shell HTTP/1.1 204 No Content date: Wed, 22 Feb 2023 02:29:44 GMT ``` Use `curl` to request `health` API. ```shell curl -i "127.0.0.1:4000/v1/influxdb/health" ``` ```shell HTTP/1.1 200 OK content-length: 0 date: Wed, 22 Feb 2023 02:30:46 GMT ``` --- ## Loki(Protocols) GreptimeDB implements the Loki Push API, so a client already writing to Loki can write to GreptimeDB by changing the endpoint. - **Endpoint**: `POST /v1/loki/api/v1/push` - **Payload encodings**: Snappy-compressed Protobuf (`Content-Type: application/x-protobuf`) or Loki JSON (`Content-Type: application/json`) - **Target table**: `loki_logs` by default, overridden with the `X-Greptime-Log-Table-Name` header - **Database**: selected with the `X-Greptime-DB-Name` header Writes only. Querying uses [SQL](/user-guide/query-data/sql.md); LogQL is not supported. Labels, headers, the resulting data model, and pipeline support are covered in [Ingest Data with Loki](/user-guide/ingest-data/for-observability/loki.md). --- ## MySQL ## Connect You can connect to GreptimeDB using MySQL via port `4002`. ```shell mysql -h -P 4002 -u -p ``` - For how to setup username and password for GreptimeDB, please refer to [Authentication](/user-guide/deployments-administration/authentication/overview.md). - If you want to use other ports for MySQL, please refer to [Protocol options](/user-guide/deployments-administration/configuration.md#protocol-options) in the configuration document. ## Table management Please refer to [Table Management](/user-guide/deployments-administration/manage-data/basic-table-operations.md). ## Ingest data Please refer to [SQL](/user-guide/ingest-data/for-iot/sql.md). ## Query data Please refer to [SQL](/user-guide/query-data/sql.md). ## Time zone GreptimeDB's MySQL protocol interface follows original MySQL on [how to deal with time zone](https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html). By default, MySQL uses its server time zone for timestamp. To override, you can set `time_zone` variable for current session using SQL statement `SET time_zone = '';`. The value of `time_zone` can be any of: - The server's time zone: `SYSTEM`. - Offset to UTC such as `+08:00`. - Any named time zone like `Europe/Berlin`. Some MySQL clients, such as Grafana MySQL data source, allow you to set the time zone for the current session. You can also use the above values when setting the time zone. You can use `SELECT` to check the current time zone settings. For example: ```sql SELECT @@system_time_zone, @@time_zone; ``` The result shows that both the system time zone and the session time zone are set to `UTC`: ```SQL +--------------------+-------------+ | @@system_time_zone | @@time_zone | +--------------------+-------------+ | UTC | UTC | +--------------------+-------------+ ``` Change the session time zone to `+1:00`: ```SQL SET time_zone = '+1:00' ``` Then you can see the difference between the system time zone and the session time zone: ```SQL SELECT @@system_time_zone, @@time_zone; +--------------------+-------------+ | @@system_time_zone | @@time_zone | +--------------------+-------------+ | UTC | +01:00 | +--------------------+-------------+ ``` For information on how the time zone affects data inserts and queries, please refer to the SQL documents in the [Ingest Data](/user-guide/ingest-data/for-iot/sql.md#time-zone) and [Query Data](/user-guide/query-data/sql.md#time-zone) sections. ## Query Timeout You can set the `max_execution_time` variable for the current session using SQL statement `SET max_execution_time = ` or `SET MAX_EXECUTION_TIME = `, which specifies the maximum time in milliseconds for a **read-only statement** to execute. The server will terminate the query if it exceeds the specified time. For example, to set the maximum query execution time to 10 seconds: ```SQL SET max_execution_time=10000; ``` --- ## OpenTelemetry (OTLP) GreptimeDB is an observability backend to consume OpenTelemetry metrics, logs, and traces natively via [OTLP/HTTP](https://opentelemetry.io/docs/specs/otlp/#otlphttp) protocol. Please refer to the [Ingest Data with OpenTelemetry](/user-guide/ingest-data/for-observability/opentelemetry.md) document for detailed information. --- ## OpenTSDB(Protocols) GreptimeDB accepts OpenTSDB metrics over HTTP, using the request and response format of OpenTSDB's `/api/put`. - **Endpoint**: `POST /v1/opentsdb/api/put` - **Body**: a single metric object or an array of them, with `metric`, `timestamp`, `value`, and `tags` Writes only. There is no OpenTSDB-compatible query endpoint; query with [SQL](/user-guide/query-data/sql.md) or [PromQL](/user-guide/query-data/promql.md). Request examples are in [Ingest Data with OpenTSDB](/user-guide/ingest-data/for-iot/opentsdb.md). --- ## Protocols GreptimeDB speaks several wire protocols. They fall into two groups. **Connection protocols** carry both writes and queries: the [HTTP API](http.md), [MySQL](mysql.md), and [PostgreSQL](postgresql.md). These pages document endpoints, authentication, and protocol-specific options. [gRPC](grpc.md) belongs here by transport but carries writes only — there is no gRPC query interface. **Ingestion protocols** accept writes from a system that already speaks them — InfluxDB line protocol, OpenTelemetry, Loki, Elasticsearch, Splunk, and OpenTSDB. The pages here cover protocol-level details; the configuration for each source is in [Ingest Data](/user-guide/ingest-data/overview.md). --- ## PostgreSQL ## Connect You can connect to GreptimeDB using PostgreSQL via port `4003`. Use the `-U` argument to specify your username. If authentication is enabled, `psql` prompts for the password: ```shell psql -h -p 4003 -U -d public ``` - To set up username and password for GreptimeDB, please refer to [Authentication](/user-guide/deployments-administration/authentication/overview.md). - If you want to use other ports for PostgreSQL, please refer to [Protocol options](/user-guide/deployments-administration/configuration.md#protocol-options) in the configuration document. ## Table management Please refer to [Table Management](/user-guide/deployments-administration/manage-data/basic-table-operations.md). ## Ingest data Please refer to [SQL](/user-guide/ingest-data/for-iot/sql.md). ## Query data Please refer to [SQL](/user-guide/query-data/sql.md). ## Time zone GreptimeDB's PostgreSQL protocol interface follows original PostgreSQL on [datatype-timezones](https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES). By default, PostgreSQL uses its server time zone for timestamp. To override, you can set `time_zone` variable for current session using SQL statement `SET TIMEZONE TO '';`. The value of `time_zone` can be any of: - A full time zone name, for example `America/New_York`. - Offset to UTC such as `+08:00`. You can use `SHOW` to check the current time zone settings. For example: ```sql SHOW VARIABLES time_zone; ``` ```sql TIME_ZONE ----------- UTC ``` Change the session time zone to `+1:00`: ```SQL SET TIMEZONE TO '+1:00' ``` For information on how the time zone affects data inserts and queries, please refer to the SQL documents in the [Ingest Data](/user-guide/ingest-data/for-iot/sql.md#time-zone) and [Query Data](/user-guide/query-data/sql.md#time-zone) sections. ## Foreign Data Wrapper GreptimeDB can be configured as a foreign data server for Postgres' built-in [FDW extension](https://www.postgresql.org/docs/current/postgres-fdw.html). This allows user to query GreptimeDB tables seamlessly from Postgres server. It's also possible to join Postgres tables with GreptimeDB tables. For example, your IoT metadata, like device information, is stored in a relational data model in Postgres. It's possible to use filter queries to find out device IDs and join with time-series data from GreptimeDB. ### Setup To setup GreptimeDB for Postgres FDW, make sure you enabled postgres protocol support in GreptimeDB and it's accessible from your Postgres server. To create and configuration GreptimeDB in Postgres, first enable the `postgres_fdw` extension. ```sql CREATE EXTENSION postgres_fdw; ``` Add GreptimeDB instance as remote server. ```sql CREATE SERVER greptimedb FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'greptimedb_host', dbname 'public', port '4003'); ``` Configure user mapping for Postgres user and GreptimeDB user. This step is required. But if you don't have authentication enabled in GreptimeDB OSS version, just fill the credential with random data. ```sql CREATE USER MAPPING FOR postgres SERVER greptimedb OPTIONS (user 'greptime', password '...'); ``` Create foreign table in Postgres to map GreptimeDB's schema. Note that you will need to use Postgres' corresponding data types for GreptimeDB's. For GreptimeDB's tables ```sql CREATE TABLE grpc_latencies ( ts TIMESTAMP TIME INDEX, host STRING, method_name STRING, latency DOUBLE, PRIMARY KEY (host, method_name) ) with('append_mode'='true'); CREATE TABLE app_logs ( ts TIMESTAMP TIME INDEX, host STRING, api_path STRING FULLTEXT INDEX, log_level STRING, log STRING FULLTEXT INDEX, PRIMARY KEY (host, log_level) ) with('append_mode'='true'); ``` The foreign table DDL is like this. You need to run them in Postgres to create these tables; ```sql CREATE FOREIGN TABLE ft_grpc_latencies ( ts TIMESTAMP, host VARCHAR, method_name VARCHAR, latency DOUBLE precision ) SERVER greptimedb OPTIONS (table_name 'grpc_latencies'); CREATE FOREIGN TABLE ft_app_logs ( ts TIMESTAMP, host VARCHAR, api_path VARCHAR, log_level VARCHAR, log VARCHAR ) SERVER greptimedb OPTIONS (table_name 'app_logs'); ``` To help you to generate statements in Postgres, we enhanced `SHOW CREATE TABLE` in GreptimeDB to dump the Postgres DDL for you. For example: ```sql SHOW CREATE TABLE grpc_latencies FOR postgres_foreign_table; ``` Note that you will need to replace server name `greptimedb` with the name you defined in `CREATE SERVER` statement. ### Run Queries You can now send query from Postgres. It's also possible to use functions that are available in both Postgres and GreptimeDB, like `date_trunc`. ```sql SELECT * FROM ft_app_logs ORDER BY ts DESC LIMIT 100; SELECT date_trunc('MINUTE', ts) as t, host, avg(latency), count(latency) FROM ft_grpc_latencies GROUP BY host, t; ``` ## Statement Timeout You can set the `statement_timeout` variable for the current session using SQL statement `SET statement_timeout = ` or `SET STATEMENT_TIMEOUT = `, which specifies the maximum time in milliseconds for a statement to execute. The server will terminate the statement if it exceeds the specified time. For example, to set the maximum execution time to 10 seconds: ```SQL SET statement_timeout=10000; ``` --- ## Splunk(Protocols) GreptimeDB implements a subset of the [Splunk HTTP Event Collector (HEC)](https://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector) protocol, so a shipper that already speaks HEC — Vector, the OpenTelemetry Collector — writes to GreptimeDB by changing the endpoint URL and the token. The base path is `/v1/splunk`; the client appends the collector path. | Endpoint | Purpose | | --- | --- | | `/services/collector/event` | Structured JSON events | | `/services/collector/raw` | Plain text, stored verbatim | Mapping: a Splunk `index` becomes a GreptimeDB table. `host`, `source`, `sourcetype`, and the keys under `fields` become tag columns and join the table's primary key. Not implemented: - Indexer acknowledgment (`/services/collector/ack`). - The `channel` parameter is accepted and ignored. - Fluent Bit's native `splunk` output, which hardcodes the request path and cannot reach `/v1/splunk`. Use [its HTTP output](/user-guide/ingest-data/for-observability/fluent-bit.md) instead. Endpoint details, response codes, and shipper configuration are in [Ingest Data with Splunk](/user-guide/ingest-data/for-observability/splunk.md). --- ## Common Table Expression (CTE) CTEs are similar to [Views](./view.md) in that they help you reduce the complexity of your queries, break down long and complex SQL statements, and improve readability and reusability. You already read a CTE in the [quickstart](/getting-started/quick-start.md#correlate-metrics-logs-and-traces) document. ## What is a Common Table Expression (CTE)? A Common Table Expression (CTE) is a temporary result set that you can reference within a `SELECT`, `INSERT`, `UPDATE`, or `DELETE` statement. CTEs help to break down complex queries into more readable parts and can be referenced multiple times within the same query. ## Basic syntax of CTE CTEs are typically defined using the `WITH` keyword. The basic syntax is as follows: ```sql WITH cte_name [(column1, column2, ...)] AS ( QUERY ) SELECT ... FROM cte_name; ``` ## Example explanation Next, we'll go through a complete example that demonstrates how to use CTEs, including data preparation, CTE creation, and usage. ### Step 1: Create example data Let's assume we have the following two tables: - `grpc_latencies`: Contains gRPC request latency data. - `app_logs`: Contains application log information. ```sql CREATE TABLE grpc_latencies ( ts TIMESTAMP TIME INDEX, host VARCHAR(255), latency FLOAT, PRIMARY KEY(host), ); INSERT INTO grpc_latencies VALUES ('2023-10-01 10:00:00', 'host1', 120), ('2023-10-01 10:00:00', 'host2', 150), ('2023-10-01 10:00:05', 'host1', 130); CREATE TABLE app_logs ( ts TIMESTAMP TIME INDEX, host VARCHAR(255), `log` TEXT, log_level VARCHAR(50), PRIMARY KEY(host, log_level), ); INSERT INTO app_logs VALUES ('2023-10-01 10:00:00', 'host1', 'Error on service', 'ERROR'), ('2023-10-01 10:00:00', 'host2', 'All services OK', 'INFO'), ('2023-10-01 10:00:05', 'host1', 'Error connecting to DB', 'ERROR'); ``` ### Step 2: Define and use CTEs We will create two CTEs to calculate the 95th percentile latency and the number of error logs, respectively. ```sql WITH metrics AS ( SELECT ts, host, approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) RANGE '5s' AS p95_latency FROM grpc_latencies ALIGN '5s' FILL PREV ), logs AS ( SELECT ts, host, COUNT(*) RANGE '5s' AS num_errors FROM app_logs WHERE log_level = 'ERROR' ALIGN '5s' BY (host) ) SELECT metrics.ts, metrics.host, metrics.p95_latency, COALESCE(logs.num_errors, 0) AS num_errors FROM metrics LEFT JOIN logs ON metrics.host = logs.host AND metrics.ts = logs.ts ORDER BY metrics.ts; ``` Output: ```sql +---------------------+-------+-------------+------------+ | ts | host | p95_latency | num_errors | +---------------------+-------+-------------+------------+ | 2023-10-01 10:00:00 | host2 | 150 | 0 | | 2023-10-01 10:00:00 | host1 | 120 | 1 | | 2023-10-01 10:00:05 | host1 | 130 | 1 | +---------------------+-------+-------------+------------+ ``` ## Detailed explanation 1. **Define CTEs**: - `metrics`: ```sql WITH metrics AS ( SELECT ts, host, approx_percentile_cont(0.95) WITHIN GROUP (ORDER BY latency) RANGE '5s' AS p95_latency FROM grpc_latencies ALIGN '5s' FILL PREV ), ``` Here we use a [range query](/user-guide/query-data/sql.md#aggregate-data-by-time-window) to calculate the 95th percentile latency for each `host` within every 5-second window. - `logs`: ```sql logs AS ( SELECT ts, host, COUNT(*) RANGE '5s' AS num_errors FROM app_logs WHERE log_level = 'ERROR' ALIGN '5s' BY (host) ) ``` Similarly, we calculate the number of error logs for each `host` within every 5-second window. 2. **Use CTEs**: The final query part: ```sql SELECT metrics.ts, metrics.host, metrics.p95_latency, COALESCE(logs.num_errors, 0) AS num_errors FROM metrics LEFT JOIN logs ON metrics.host = logs.host AND metrics.ts = logs.ts ORDER BY metrics.ts; ``` We perform a left join on the two CTE result sets to get a comprehensive analysis result. ## Using TQL in CTEs GreptimeDB supports using [TQL](/reference/sql/tql.md) (Telemetry Query Language) within CTEs, allowing you to combine PromQL's time-series computation with SQL's powerful post-processing capabilities like filtering, joining, and aggregation. ### Basic syntax ```sql WITH cte_name AS ( TQL EVAL (start, end, step) promql_expression AS value_alias ) SELECT * FROM cte_name; ``` ### Key points 1. **Column naming**: - The time index column name varies depending on your table schema (e.g., `ts` for custom tables, `greptime_timestamp` for Prometheus remote write) - The value column name depends on the PromQL expression and may be unpredictable, so it's better to use value aliasing with `AS` in TQL to ensure predictable column names: `TQL EVAL (...) expression AS my_value` - **Important**: In general, avoid using column aliases in CTE definitions (for example, `WITH cte_name (ts, val) AS (...)`) because TQL EVAL results can have variable column count and order, especially in Prometheus scenarios where tags can be dynamically added or removed. They are best reserved for cases where the result shape is stable, such as a simple `CREATE FLOW` TQL CTE. 2. **Supported commands**: Only `TQL EVAL` is supported within CTEs. `TQL ANALYZE` and `TQL EXPLAIN` cannot be used in CTEs. 3. **Lookback parameter**: The optional fourth parameter controls the lookback duration (default: 5 minutes). 4. **Using TQL CTEs in `CREATE FLOW`**: GreptimeDB supports `WITH ... AS (TQL EVAL ...)` in `CREATE FLOW`, but only in the simplest form: a single TQL CTE followed by `SELECT * FROM `. Extra SQL CTEs, filters, joins, or custom projections are not supported in flow definitions. ### Examples The following examples use Kubernetes monitoring metrics to demonstrate practical use cases. #### Filtering TQL results with SQL This example calculates CPU usage per pod using PromQL, then filters and sorts the results using SQL: ```sql WITH cpu_usage AS ( TQL EVAL (now() - interval '1' hour, now(), '5m') sum by (namespace, pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m])) AS cpu_cores ) SELECT greptime_timestamp as ts, namespace, pod, cpu_cores FROM cpu_usage WHERE cpu_cores > 0.5 ORDER BY cpu_cores DESC; ``` #### Correlating multiple metrics This example demonstrates a capability that PromQL alone cannot achieve: joining different metrics for correlation analysis. It correlates CPU usage with request rates to analyze resource efficiency: ```sql WITH cpu_data AS ( TQL EVAL (now() - interval '1' hour, now(), '5m') sum by (pod) (rate(container_cpu_usage_seconds_total{container!=""}[5m])) AS cpu_cores ), request_data AS ( TQL EVAL (now() - interval '1' hour, now(), '5m') sum by (pod) (rate(http_requests_total[5m])) AS req_per_sec ) SELECT c.greptime_timestamp as ts, c.pod, c.cpu_cores, r.req_per_sec FROM cpu_data c JOIN request_data r ON c.greptime_timestamp = r.greptime_timestamp AND c.pod = r.pod WHERE r.req_per_sec > 1; ``` When joining multiple TQL CTEs, ensure: - The `by (...)` clauses contain matching dimensions - JOIN conditions include both timestamp and label columns ## Summary With CTEs, you can break down complex SQL queries into more manageable and understandable parts. In this example, we created two CTEs to calculate the 95th percentile latency and the number of error logs separately and then merged them into the final query for analysis. TQL support in CTEs extends this capability by allowing PromQL-style queries to be integrated seamlessly with SQL processing. Read more about [WITH](/reference/sql/with.md) and [TQL](/reference/sql/tql.md). For more TQL + CTE examples, see the blog post [When PromQL Meets SQL: Hybrid Queries for Kubernetes Monitoring](https://greptime.com/blogs/2026-01-08-tql-alias-k8s-monitoring). --- ## Jaeger (Experimental) :::warning The Jaeger query APIs are currently in the experimental stage and may be adjusted in future versions. ::: GreptimeDB currently supports the following [Jaeger](https://www.jaegertracing.io/) query interfaces: - `/api/services`: Get all services. - `/api/operations?service={service}`: Get all operations for a service. - `/api/services/{service}/operations`: Get all operations for a service. - `/api/traces`: Get traces by query parameters. - `/api/traces/{trace_id}`: Get a trace by its ID. You can use [Grafana's Jaeger plugin](https://grafana.com/docs/grafana/latest/datasources/jaeger/)(Recommended) or [Jaeger UI](https://github.com/jaegertracing/jaeger-ui) to query traces data in GreptimeDB. When using Jaeger UI, you can set the `proxyConfig` in `packages/jaeger-ui/vite.config.mts` to the GreptimeDB address, for example: ```ts const proxyConfig = { target: 'http://localhost:4000/v1/jaeger', secure: false, changeOrigin: true, ws: true, xfwd: true, }; ``` Currently, GreptimeDB exposes the Jaeger HTTP APIs under the `/v1/jaeger` endpoint. ## Quick Start We will use the Jaeger plugin in Grafana as an example to demonstrate how to query traces data in GreptimeDB. Before starting, please ensure that you have properly started GreptimeDB. ### Start an application to generate traces data and write it to GreptimeDB You can refer to the [OpenTelemetry official documentation](https://opentelemetry.io/docs/languages/) to choose any programming language you are familiar with to generate traces data. You can also refer to the [Configure OpenTelemetry Collector](/user-guide/traces/read-write.md#opentelemetry-collector) document. ### Configure the Grafana Jaeger plugin 1. Open Grafana and add a Jaeger data source: ![Add Jaeger data source](/add-jaeger-data-source.jpg) 2. Fill in the Jaeger address according to your actual situation, then **Save and Test**. For example: ``` http://localhost:4000/v1/jaeger ``` 3. Use Grafana's Jaeger Explore to view the data: ![Jaeger Explore](/jaeger-explore.png) ### Custom table By default, the jaeger compatible API will use the default table name `opentelemetry_traces` for its data. If you have multiple tables for trace data, you can update this behaviour with HTTP header `x-greptime-trace-table-name` and provide table name as the value. This can be configured via Grafana data source settings. --- ## Log Query (Experimental) :::warning The log query endpoint feature is currently experimental and may change in future releases. ::: GreptimeDB provides a dedicated HTTP endpoint to query log data. This feature allows you to search and process log entries using a simple query interface. This is an add-on feature to existing GreptimeDB capabilities like SQL queries and Flow computations. You can still use your existing tools and workflows to query log data like before. ## Endpoint ```http POST /v1/logs ``` ## Headers - [Authorization](/user-guide/protocols/http.md#authentication) - `Content-Type`: `application/json` ## Request Format The request body should be a JSON object (this is subject to change in patch version within the experimental phase). For the latest request format, please refer to the [implementation](https://github.com/GreptimeTeam/greptimedb/blob/main/src/log-query/src/log_query.rs): ## Response This endpoint has the same response format as the SQL query endpoint. Please refer to the [SQL query response](/user-guide/protocols/http/#response) for more details. ## Limitations - Default result limit: 1000 entries. Set `limit.fetch` in the request to override it. - Requires a time index column. String search filters apply to string/text expressions, while typed comparison filters can be applied to compatible numeric, boolean, and string expressions. ## Example The following example demonstrates how to query log data using the log query endpoint (notice that in this experimental phase the following example might be outdated). ```shell curl -X "POST" "http://localhost:4000/v1/logs" \ -H "Authorization: Basic " \ -H "Content-Type: application/json" \ -d $' { "table": { "catalog_name": "greptime", "schema_name": "public", "table_name": "my_logs" }, "time_filter": { "start": "2025-01-23" }, "limit": { "fetch": 1 }, "columns": [ "message" ], "filters": { "Single": { "expr": { "NamedIdent": "message" }, "filters": [ { "Contains": "production" } ] } }, "context": "None", "exprs": [] } ' ``` In this query, we are searching for log entries in the `greptime.public.my_logs` table that contain the word `production` in `message` field. We also specify the time filter to fetch logs in `2025-01-23`, and limit the result to 1 entry. The response will be similar to the following: ```json { "output": [ { "records": { "schema": { "column_schemas": [ { "name": "message", "data_type": "String" } ] }, "rows": [ [ "Everything is in production" ] ], "total_rows": 1 } } ], "execution_time_ms": 30 } ``` --- ## Query Data GreptimeDB exposes several query interfaces over the same tables. Which one to use depends on the signal and on the tool doing the asking. ## Query languages - [SQL](./sql.md) — queries across metrics, logs, and traces, including range queries. [Views](./view.md) and [common table expressions](./cte.md) factor out a query you write repeatedly. - [PromQL](./promql.md) — metric queries through the Prometheus HTTP API, or `TQL` inside SQL. - [Jaeger API](./jaeger.md) — trace queries from Jaeger UI or Grafana. - [Log Query](./log-query.md) — a dedicated HTTP endpoint for log search. Experimental. ## Clients and drivers GreptimeDB speaks the [MySQL](/user-guide/protocols/mysql.md) and [PostgreSQL](/user-guide/protocols/postgresql.md) wire protocols, so existing SQL drivers connect to it without a GreptimeDB-specific client. [SQL Tools](/reference/sql-tools.md) lists the drivers and their connection settings. ## Query files that were never ingested GreptimeDB can run SQL over external data files in place. See [Query External Data](./query-external-data.md). --- ## Prometheus Query Language GreptimeDB can be used as a drop-in replacement for Prometheus in Grafana, because GreptimeDB supports PromQL (Prometheus Query Language). GreptimeDB has reimplemented PromQL natively in Rust and exposes the ability to several interfaces, including the HTTP API of Prometheus, the HTTP API of GreptimeDB, and the SQL interface. ## Prometheus' HTTP API GreptimeDB has implemented a set of Prometheus compatible APIs under HTTP context `/v1/prometheus/`: - Instant queries `/api/v1/query` - Range queries `/api/v1/query_range` - Series `/api/v1/series` - Label names `/api/v1/labels` - Label values `/api/v1/label//values` It shares same input and output format with original Prometheus HTTP API. You can also use GreptimeDB as an in-place replacement of Prometheus. For example in Grafana Prometheus data source, set `http://localhost:4000/v1/prometheus/` as context root of Prometheus URL. Consult [Prometheus documents](https://prometheus.io/docs/prometheus/latest/querying/api) for usage of these API. You can use additional query parameter `db` to specify GreptimeDB database name. For example, the following query will return the CPU usage of the `process_cpu_seconds_total` metric in the `public` database: ```shell curl -X POST \ -H 'Authorization: Basic ' \ --data-urlencode 'query=irate(process_cpu_seconds_total[1h])' \ --data-urlencode 'start=2024-11-24T00:00:00Z' \ --data-urlencode 'end=2024-11-25T00:00:00Z' \ --data-urlencode 'step=1h' \ 'http://localhost:4000/v1/prometheus/api/v1/query_range?db=public' ``` If authentication is enabled in GreptimeDB, the authentication header is required. Refer to the [authentication documentation](/user-guide/protocols/http.md#authentication) for more details. You need to either set it in the HTTP URL query param `db` like the example above, or set it using `--header 'x-greptime-db-name: '` as HTTP header. The query string parameters for the API are identical to those of the original [Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries), with the exception of the additional `db` parameter, which specifies the GreptimeDB database name. The output format is compatible with the Prometheus API: ```json { "status": "success", "data": { "resultType": "matrix", "result": [ { "metric": { "job": "node", "instance": "node_exporter:9100", "__name__": "process_cpu_seconds_total" }, "values": [ [ 1732618800, "0.0022222222222222734" ], [ 1732622400, "0.0009999999999999788" ], [ 1732626000, "0.0029999999999997585" ], [ 1732629600, "0.002222222222222175" ] ] } ] } } ``` ## SQL GreptimeDB also extends SQL grammar to support PromQL. You can start with the `TQL` (Time-series Query Language) keyword to write parameters and queries. The grammar looks like this: ```sql TQL [EVAL|EVALUATE] (, , ) ``` `` specifies the query start range and `` specifies the end time. `` identifies the query resolution step width. All of them can either be an unquoted number (represent UNIX timestamp for `` and ``, and duration in seconds for ``), or a quoted string (represent an RFC3339 timestamp for `` and ``, and duration in string format for ``). For example: ```sql TQL EVAL (1676738180, 1676738780, '10s') sum(some_metric) ``` You can write the above command in all places that support SQL, including the GreptimeDB HTTP API, SDK, PostgreSQL and MySQL client etc. ## GreptimeDB's extensions to PromQL ### Specifying value field Based on the table model, GreptimeDB supports multiple fields in a single table(or metric, in the context of Prometheus). Queries will run on every fields by default. Or you can use the special filter `__field__` to query a specific field(s): ```promql metric{__field__="field1"} ``` Exclude or regex are also supported: ```promql metric{__field__!="field1"} metric{__field__=~"field_1|field_2"} metric{__field__!~"field_1|field_2"} ``` ### Cross-database query Greptime has its own database concept. In order to run cross-database query, you can use `__database__` matcher to specify the database name. ```promql metric{__database__="mydatabase"} ``` Note that only `=` is supported for database matcher. ## Limitations Though GreptimeDB supports a rich set of data types, the PromQL implementation is still limited to the following types: - timestamp: `Timestamp` - tag: `String` - value: `Double` We have over 90% promql supported in GreptimeDB. Here attaches the compatibility list. You can also check our latest compliance report in this [tracking issue](https://github.com/GreptimeTeam/greptimedb/issues/1042). ### Literal Both string and float literals are supported, with the same [rule](https://prometheus.io/docs/prometheus/latest/querying/basics/#literals) as PromQL. ### Selector Both instant and range selector are supported. Label matching on the metric name (`__name__`) has the following restrictions: - In [`TQL`](#sql), only equal-matching is supported, for example `{__name__="request_count"}`. - The [Prometheus HTTP API](#prometheus-http-api) also supports regex-matching and negative matching, for example `{__name__=~"request_.*"}` or `{__name__!="request_count", host="h1"}`. These matchers only match tables created by the metric engine, such as tables written through Prometheus Remote Write. Time duration and offset are supported, but `@` modifier is not supported yet. When selecting non-existent columns, they will be treated as columns filled with empty string values (`""`). This behavior aligns with both Prometheus and VictoriaMetrics. For selectors referencing non-existent columns, the behavior aligns with Prometheus: no error is raised, and the selector is silently ignored. However, for `__name__` selectors referencing non-existent metrics (or equivalent forms), GreptimeDB will report an error. ### Timestamp precision The timestamp precision in PromQL is limited by its query syntax, only supporting calculations up to millisecond precision. However, GreptimeDB supports storing high-precision timestamps, such as microseconds and nanoseconds. When using PromQL for calculations, these high-precision timestamps are implicitly converted to millisecond precision. ### Binary - Supported: | Operator | | :------- | | add | | sub | | mul | | div | | mod | | eqlc | | neq | | gtr | | lss | | gte | | lte | | power | | atan2 | | and | | or | | unless | - Unsupported: None ### Aggregators - Supported: | Aggregator | Example | | :----------- | :------------------------------------------- | | sum | `sum by (foo)(metric)` | | avg | `avg by (foo)(metric)` | | min | `min by (foo)(metric)` | | max | `max by (foo)(metric)` | | stddev | `stddev by (foo)(metric)` | | stdvar | `stdvar by (foo)(metric)` | | topk | `topk(3, rate(instance_cpu_time_ns[5m]))` | | bottomk | `bottomk(3, rate(instance_cpu_time_ns[5m]))` | | count_values | `count_values("version", build_version)` | | count | `count (metric)` | | quantile | `quantile(0.9, cpu_usage)` | - Unsupported: | Aggregator | Progress | | :--------- | :------- | | grouping | TBD | ### Instant Functions - Supported: | Function | Example | | :----------------- | :-------------------------------- | | abs | `abs(metric)` | | ceil | `ceil(metric)` | | exp | `exp(metric)` | | ln | `ln(metric)` | | log2 | `log2(metric)` | | log10 | `log10(metric)` | | sqrt | `sqrt(metric)` | | acos | `acos(metric)` | | asin | `asin(metric)` | | atan | `atan(metric)` | | sin | `sin(metric)` | | cos | `cos(metric)` | | tan | `tan(metric)` | | acosh | `acosh(metric)` | | asinh | `asinh(metric)` | | atanh | `atanh(metric)` | | sinh | `sinh(metric)` | | cosh | `cosh(metric)` | | scalar | `scalar(metric)` | | tanh | `tanh(metric)` | | timestamp | `timestamp(metric)` | | sort | `sort(http_requests_total)` | | sort_desc | `sort_desc(http_requests_total)` | | histogram_quantile | `histogram_quantile(phi, metric)` | | predict_linear | `predict_linear(metric[5m], 120)` | | absent | `absent(nonexistent{job="myjob"})`| | sgn | `sgn(metric)` | | pi | `pi()` | | deg | `deg(metric)` | | rad | `rad(metric)` | | floor | `floor(metric)` | | clamp | `clamp(metric, 0, 12)` | | clamp_max | `clamp_max(metric, 12)` | | clamp_min | `clamp_min(metric, 0)` | - Unsupported: | Function | Progress / Example | | :------------------------- | :----------------- | | *other multiple input fns* | TBD | ### Range Functions - Supported: | Function | Example | | :----------------- | :----------------------------- | | idelta | `idelta(metric[5m])` | | \_over_time | `count_over_time(metric[5m])` | | stddev_over_time | `stddev_over_time(metric[5m])` | | stdvar_over_time | `stdvar_over_time(metric[5m])` | | changes | `changes(metric[5m])` | | delta | `delta(metric[5m])` | | rate | `rate(metric[5m])` | | deriv | `deriv(metric[5m])` | | increase | `increase(metric[5m])` | | irate | `irate(metric[5m])` | | resets | `resets(metric[5m])` | - Unsupported: None ### Label & Other Functions - Supported: | Function | Example | | :------------ | :------------------------------------------------------------------------------------------------ | | label_join | `label_join(up{job="api-server",src1="a",src2="b",src3="c"}, "foo", ",", "src1", "src2", "src3")` | | label_replace | `label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")` | | sort_by_label | `sort_by_label(metric, "foo", "bar")` | | sort_by_label_desc | `sort_by_label_desc(metric, "foo", "bar")` | - Unsupported: None --- ## Query External Data ## Query on a file Currently, we support queries on `Parquet`, `CSV`, `ORC`, and `NDJson` format file(s). We use the [Taxi Zone Lookup Table](https://d37ci6vzurychx.cloudfront.net/misc/taxi+_zone_lookup.csv) data as an example. ```bash mkdir -p greptimedb_data/copy curl "https://d37ci6vzurychx.cloudfront.net/misc/taxi+_zone_lookup.csv" -o greptimedb_data/copy/taxi+_zone_lookup.csv ``` Create an external table: ```sql CREATE EXTERNAL TABLE taxi_zone_lookup with (location='taxi+_zone_lookup.csv',format='csv'); ``` :::tip NOTE In standalone deployments, external table locations that reference local files are sandboxed to the `storage.copy_root` directory (default `/copy`), so this example downloads the file into `greptimedb_data/copy` and uses a location relative to that directory. In distributed deployments, local file locations are not supported. See [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for details. ::: You can check the schema of the external table like follows: ```sql DESC TABLE taxi_zone_lookup; ``` ```sql +--------------------+----------------------+------+------+--------------------------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+----------------------+------+------+--------------------------+---------------+ | LocationID | Int64 | | YES | | FIELD | | Borough | String | | YES | | FIELD | | Zone | String | | YES | | FIELD | | service_zone | String | | YES | | FIELD | | greptime_timestamp | TimestampMillisecond | PRI | NO | 1970-01-01 00:00:00+0000 | TIMESTAMP | +--------------------+----------------------+------+------+--------------------------+---------------+ 4 rows in set (0.00 sec) ``` :::tip Note Here, you may notice there is a `greptime_timestamp` column, which doesn't exist in the file. This is because when creating an external table, if we didn't specify a `TIME INDEX` column, the `greptime_timestamp` column is automatically added as the `TIME INDEX` column with a default value of `1970-01-01 00:00:00+0000`. You can find more details in the [create](/reference/sql/create.md#create-external-table) document. ::: Now you can query on the external table: ```sql SELECT `Zone`, `Borough` FROM taxi_zone_lookup LIMIT 5; ``` ```sql +-------------------------+---------------+ | Zone | Borough | +-------------------------+---------------+ | Newark Airport | EWR | | Jamaica Bay | Queens | | Allerton/Pelham Gardens | Bronx | | Alphabet City | Manhattan | | Arden Heights | Staten Island | +-------------------------+---------------+ ``` ## Query on a directory Let's download some data: ```bash mkdir -p greptimedb_data/copy/external curl "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2022-01.parquet" -o greptimedb_data/copy/external/yellow_tripdata_2022-01.parquet curl "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2022-02.parquet" -o greptimedb_data/copy/external/yellow_tripdata_2022-02.parquet ``` Verify the download: ```bash ls -l greptimedb_data/copy/external total 165368 -rw-r--r-- 1 wenyxu wheel 38139949 Apr 28 14:35 yellow_tripdata_2022-01.parquet -rw-r--r-- 1 wenyxu wheel 45616512 Apr 28 14:36 yellow_tripdata_2022-02.parquet ``` Create the external table: ```sql CREATE EXTERNAL TABLE yellow_tripdata with(location='external/',format='parquet'); ``` Run queries: ```sql SELECT count(*) FROM yellow_tripdata; ``` ```sql +-----------------+ | COUNT(UInt8(1)) | +-----------------+ | 5443362 | +-----------------+ 1 row in set (0.48 sec) ``` ```sql SELECT * FROM yellow_tripdata LIMIT 5; ``` ```sql +----------+----------------------+-----------------------+-----------------+---------------+------------+--------------------+--------------+--------------+--------------+-------------+-------+---------+------------+--------------+-----------------------+--------------+----------------------+-------------+---------------------+ | VendorID | tpep_pickup_datetime | tpep_dropoff_datetime | passenger_count | trip_distance | RatecodeID | store_and_fwd_flag | PULocationID | DOLocationID | payment_type | fare_amount | extra | mta_tax | tip_amount | tolls_amount | improvement_surcharge | total_amount | congestion_surcharge | airport_fee | greptime_timestamp | +----------+----------------------+-----------------------+-----------------+---------------+------------+--------------------+--------------+--------------+--------------+-------------+-------+---------+------------+--------------+-----------------------+--------------+----------------------+-------------+---------------------+ | 1 | 2022-02-01 00:06:58 | 2022-02-01 00:19:24 | 1 | 5.4 | 1 | N | 138 | 252 | 1 | 17 | 1.75 | 0.5 | 3.9 | 0 | 0.3 | 23.45 | 0 | 1.25 | 1970-01-01 00:00:00 | | 1 | 2022-02-01 00:38:22 | 2022-02-01 00:55:55 | 1 | 6.4 | 1 | N | 138 | 41 | 2 | 21 | 1.75 | 0.5 | 0 | 6.55 | 0.3 | 30.1 | 0 | 1.25 | 1970-01-01 00:00:00 | | 1 | 2022-02-01 00:03:20 | 2022-02-01 00:26:59 | 1 | 12.5 | 1 | N | 138 | 200 | 2 | 35.5 | 1.75 | 0.5 | 0 | 6.55 | 0.3 | 44.6 | 0 | 1.25 | 1970-01-01 00:00:00 | | 2 | 2022-02-01 00:08:00 | 2022-02-01 00:28:05 | 1 | 9.88 | 1 | N | 239 | 200 | 2 | 28 | 0.5 | 0.5 | 0 | 3 | 0.3 | 34.8 | 2.5 | 0 | 1970-01-01 00:00:00 | | 2 | 2022-02-01 00:06:48 | 2022-02-01 00:33:07 | 1 | 12.16 | 1 | N | 138 | 125 | 1 | 35.5 | 0.5 | 0.5 | 8.11 | 0 | 0.3 | 48.66 | 2.5 | 1.25 | 1970-01-01 00:00:00 | +----------+----------------------+-----------------------+-----------------+---------------+------------+--------------------+--------------+--------------+--------------+-------------+-------+---------+------------+--------------+-----------------------+--------------+----------------------+-------------+---------------------+ 5 rows in set (0.11 sec) ``` :::tip Note The query result includes the value of the `greptime_timestamp` column, although it does not exist in the original file. All these column values are `1970-01-01 00:00:00+0000`, because when we create an external table, the `greptime_timestamp` column is automatically added with a default value of `1970-01-01 00:00:00+0000`. You can find more details in the [create](/reference/sql/create.md#create-external-table) document. ::: ## Query nested JSON fields Use `FORMAT = 'JSON'` for newline-delimited JSON (NDJSON), with one JSON object per line. In a standalone deployment using the default data home, create a sample file under `greptimedb_data/copy`: ```bash mkdir -p greptimedb_data/copy/logs cat > greptimedb_data/copy/logs/requests.json <<'EOF' {"host":"web-01","request":{"status":500,"path":"/api","client":{"ip":"10.0.0.1"}}} {"host":"web-02","request":{"status":200,"path":"/health","client":{"ip":"10.0.0.2"}}} EOF ``` Omit the column definitions to infer the schema from the files: ```sql CREATE EXTERNAL TABLE logs WITH (LOCATION = 'logs/', FORMAT = 'JSON'); DESC TABLE logs; ``` The inferred column types are: ```text host String request Struct<"client": Struct<"ip": String>, "path": String, "status": Int64> greptime_timestamp TimestampMillisecond ``` The nested `request` object is a `Struct` column. The automatically added `greptime_timestamp` column is the time index and has the default value `1970-01-01 00:00:00+0000`. Use brackets to select nested fields and filter by their values: ```sql SELECT host, request['status'] AS status, request['path'] AS path FROM logs WHERE request['status'] >= 500; ``` ```text +--------+--------+------+ | host | status | path | +--------+--------+------+ | web-01 | 500 | /api | +--------+--------+------+ ``` Chain brackets for deeper fields, or use the equivalent `get_field()` function: ```sql SELECT host, request['client']['ip'] AS client_ip FROM logs; SELECT host, get_field(request, 'status') AS status FROM logs; ``` `request['status']` accesses a field inside the `request` struct. In contrast, `"request.status"` refers to a top-level column whose name contains a dot. Schema inference preserves nested objects as structs; it does not flatten them into dotted column names or convert them into `JSON` or `JSON2` columns. --- ## SQL(Query-data) GreptimeDB supports full SQL for querying data from a database. In this document, we will use the `monitor` table to demonstrate how to query data. For instructions on creating the `monitor` table and inserting data into it, Please refer to [table management](/user-guide/deployments-administration/manage-data/basic-table-operations.md#create-a-table) and [Ingest Data](/user-guide/ingest-data/for-iot/sql.md). ## Basic query The query is represented by the `SELECT` statement. For example, the following query selects all data from the `monitor` table: ```sql SELECT * FROM monitor; ``` The query result looks like the following: ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2022-11-03 03:39:57 | 0.1 | 0.4 | | 127.0.0.1 | 2022-11-03 03:39:58 | 0.5 | 0.2 | | 127.0.0.2 | 2022-11-03 03:39:58 | 0.2 | 0.3 | +-----------+---------------------+------+--------+ 3 rows in set (0.00 sec) ``` Functions are also supported in the `SELECT` field list. For example, you can use the `count()` function to retrieve the total number of rows in the table: ```sql SELECT count(*) FROM monitor; ``` ```sql +-----------------+ | COUNT(UInt8(1)) | +-----------------+ | 3 | +-----------------+ ``` The `avg()` function returns the average value of a certain field: ```sql SELECT avg(cpu) FROM monitor; ``` ```sql +---------------------+ | AVG(monitor.cpu) | +---------------------+ | 0.26666666666666666 | +---------------------+ 1 row in set (0.00 sec) ``` You can also select only the result of a function. For example, you can extract the day of the year from a timestamp. The `DOY` in the SQL statement is the abbreviation of `day of the year`: ```sql SELECT date_part('DOY', '2021-07-01 00:00:00'); ``` Output: ```sql +----------------------------------------------------+ | date_part(Utf8("DOY"),Utf8("2021-07-01 00:00:00")) | +----------------------------------------------------+ | 182 | +----------------------------------------------------+ 1 row in set (0.003 sec) ``` The parameters and results of date functions align with the SQL client's time zone. For example, when the client's time zone is set to `+08:00`, the results of the two queries below are the same: ```sql select to_unixtime('2024-01-02 00:00:00'); select to_unixtime('2024-01-02 00:00:00+08:00'); ``` Please refer to [SELECT](/reference/sql/select.md) and [Functions](/reference/sql/functions/overview.md) for more information. ## Limit the number of rows returned Time series data is typically massive. To save bandwidth and improve query performance, you can use the `LIMIT` clause to restrict the number of rows returned by the `SELECT` statement. For example, the following query limits the number of rows returned to 10: ```sql SELECT * FROM monitor LIMIT 10; ``` ## Filter data You can use the `WHERE` clause to filter the rows returned by the `SELECT` statement. Filtering data by tags or time index is efficient and common in time series scenarios. For example, the following query filter data by tag `host`: ```sql SELECT * FROM monitor WHERE host='127.0.0.1'; ``` The following query filter data by time index `ts`, and returns the data after `2022-11-03 03:39:57`: ```sql SELECT * FROM monitor WHERE ts > '2022-11-03 03:39:57'; ``` You can also use the `AND` keyword to combine multiple constraints: ```sql SELECT * FROM monitor WHERE host='127.0.0.1' AND ts > '2022-11-03 03:39:57'; ``` ### Filter by time index Filtering data by the time index is a crucial feature in time series databases. When working with Unix time values, the database treats them based on the type of the column value. For instance, if the `ts` column in the `monitor` table has a value type of `TimestampMillisecond`, you can use the following query to filter the data: The Unix time value `1667446797000` corresponds to the `TimestampMillisecond` type。 ```sql SELECT * FROM monitor WHERE ts > 1667446797000; ``` When working with a Unix time value that doesn't have the precision of the column value, you need to use the `::` syntax to specify the type of the time value. This ensures that the database correctly identifies the type. For example, `1667446797` represents a timestamp in seconds, which is different from the default millisecond timestamp of the `ts` column. You need to specify its type as `TimestampSecond` using the `::TimestampSecond` syntax. This informs the database that the value `1667446797` should be treated as a timestamp in seconds. ```sql SELECT * FROM monitor WHERE ts > 1667446797::TimestampSecond; ``` For the supported time data types, please refer to [Data Types](/reference/sql/data-types.md#date-and-time-types). When using standard `RFC3339` or `ISO8601` string literals, you can directly use them in the filter condition since the precision is clear. ```sql SELECT * FROM monitor WHERE ts > '2022-07-25 10:32:16.408'; ``` Time and date functions are also supported in the filter condition. For example, use the `now()` function and the `INTERVAL` keyword to retrieve data from the last 5 minutes: ```sql SELECT * FROM monitor WHERE ts >= now() - '5 minutes'::INTERVAL; ``` For date and time functions, please refer to [Functions](/reference/sql/functions/overview.md) for more information. ### Time zone A string literal timestamp without time zone information will be interpreted based on the local time zone of the SQL client. For example, the following two queries are equivalent when the client time zone is `+08:00`: ```sql SELECT * FROM monitor WHERE ts > '2022-07-25 10:32:16.408'; SELECT * FROM monitor WHERE ts > '2022-07-25 10:32:16.408+08:00'; ``` All the timestamp column values in query results are formatted based on the client time zone. For example, the following code shows the same `ts` value formatted in the client time zones `UTC` and `+08:00` respectively. ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2023-12-31 16:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ ``` ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2024-01-01 00:00:00 | 0.5 | 0.1 | +-----------+---------------------+------+--------+ ``` ### Functions GreptimeDB offers an extensive suite of built-in functions and aggregation capabilities tailored to meet the demands of data analytics. Its features include: - A comprehensive set of functions inherited from Apache Datafusion query engine, featuring a selection of date/time functions that adhere to Postgres naming conventions and behaviour. - Logical data type operations for JSON, Geolocation, and other specialized data types. - Advanced full-text matching capabilities. See [Functions reference](/reference/sql/functions/overview.md) for more details. ## Order By The order of the returned data is not guaranteed. You need to use the `ORDER BY` clause to sort the returned data. For example, in time series scenarios, it is common to use the time index column as the sorting key to arrange the data chronologically: ```sql -- ascending order by ts SELECT * FROM monitor ORDER BY ts ASC; ``` ```sql -- descending order by ts SELECT * FROM monitor ORDER BY ts DESC; ``` ## Remote dynamic filter pushdown GreptimeDB enables remote dynamic filter pushdown by default for distributed SQL queries. It is mainly used by distributed join queries. When a join builds a runtime dynamic filter from one side of the join, the Frontend can propagate that filter to Datanodes so scans on the other side may prune data earlier. Some other operators, such as TopK (`ORDER BY ... LIMIT`), may also produce dynamic filters in eligible plans. This is a best-effort performance optimization and does not change query results. It only applies when the distributed query plan contains a dynamic filter that can be sent to remote scans. If the query plan has no dynamic filter, or if the filter cannot be encoded or applied safely, GreptimeDB runs the query without remote dynamic filter pushdown. To disable this optimization for one HTTP SQL request, set the `query.enable_remote_dynamic_filter_pushdown` hint to `false`: ```bash curl -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'x-greptime-hints: query.enable_remote_dynamic_filter_pushdown=false' \ --data-urlencode "sql=SELECT m.* FROM monitor m JOIN host_info h ON m.host = h.host WHERE h.region = 'us-west'" \ http://localhost:4000/v1/sql ``` The option defaults to `true`. Setting it to `false` disables Frontend-to-Datanode remote dynamic filter propagation for the current query only. It also applies to standalone deployments when the query is executed through the local Frontend-to-Datanode region query path. Currently, there is no persistent Frontend or Datanode configuration option for changing this default. It does not disable local dynamic filter optimizations inside one execution node. For more information about request hints, see [HTTP hints](/user-guide/protocols/http.md#hints). ## `CASE` Expression You can use the `CASE` statement to perform conditional logic within your queries. For example, the following query returns the status of the CPU based on the value of the `cpu` field: ```sql SELECT host, ts, CASE WHEN cpu > 0.5 THEN 'high' WHEN cpu > 0.3 THEN 'medium' ELSE 'low' END AS cpu_status FROM monitor; ``` The result is shown below: ```sql +-----------+---------------------+------------+ | host | ts | cpu_status | +-----------+---------------------+------------+ | 127.0.0.1 | 2022-11-03 03:39:57 | low | | 127.0.0.1 | 2022-11-03 03:39:58 | medium | | 127.0.0.2 | 2022-11-03 03:39:58 | low | +-----------+---------------------+------------+ 3 rows in set (0.01 sec) ``` For more information, please refer to [CASE](/reference/sql/case.md). ## Aggregate data by tag You can use the `GROUP BY` clause to group rows that have the same values into summary rows. The average CPU usage grouped by host: ```sql SELECT host, avg(cpu) FROM monitor GROUP BY host; ``` ```sql +-----------+------------------+ | host | AVG(monitor.cpu) | +-----------+------------------+ | 127.0.0.2 | 0.2 | | 127.0.0.1 | 0.3 | +-----------+------------------+ 2 rows in set (0.00 sec) ``` Please refer to [GROUP BY](/reference/sql/group_by.md) for more information. ### Find the latest data of time series To find the latest point of each time series, you can use `DISTINCT ON` together with `ORDER BY` like in [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/distinct). ```sql SELECT DISTINCT ON (host) * FROM monitor ORDER BY host, ts DESC; ``` ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2022-11-03 03:39:58 | 0.5 | 0.2 | | 127.0.0.2 | 2022-11-03 03:39:58 | 0.2 | 0.3 | +-----------+---------------------+------+--------+ 2 rows in set (0.00 sec) ``` ## Aggregate data by time window GreptimeDB supports [Range Query](/reference/sql/range.md) to aggregate data by time window. Suppose we have the following data in the [`monitor` table](/user-guide/deployments-administration/manage-data/basic-table-operations.md#create-a-table): ```sql +-----------+---------------------+------+--------+ | host | ts | cpu | memory | +-----------+---------------------+------+--------+ | 127.0.0.1 | 2023-12-13 02:05:41 | 0.5 | 0.2 | | 127.0.0.1 | 2023-12-13 02:05:46 | NULL | NULL | | 127.0.0.1 | 2023-12-13 02:05:51 | 0.4 | 0.3 | | 127.0.0.2 | 2023-12-13 02:05:41 | 0.3 | 0.1 | | 127.0.0.2 | 2023-12-13 02:05:46 | NULL | NULL | | 127.0.0.2 | 2023-12-13 02:05:51 | 0.2 | 0.4 | +-----------+---------------------+------+--------+ ``` The following query returns the average CPU usage in a 10-second time range and calculates it every 5 seconds: ```sql SELECT ts, host, avg(cpu) RANGE '10s' FILL LINEAR FROM monitor ALIGN '5s' TO '2023-12-01T00:00:00' BY (host) ORDER BY ts ASC; ``` 1. `avg(cpu) RANGE '10s' FILL LINEAR` is a Range expression. `RANGE '10s'` specifies that the time range of the aggregation is 10s, and `FILL LINEAR` specifies that if there is no data within a certain aggregation time, use the `LINEAR` method to fill it. 2. `ALIGN '5s'` specifies the that data statistics should be performed in steps of 5s. 3. `TO '2023-12-01T00:00:00` specifies the origin alignment time. The default value is Unix time 0. 4. `BY (host)` specifies the aggregate key. If the `BY` keyword is omitted, the primary key of the data table is used as the aggregate key by default. 5. `ORDER BY ts ASC` specifies the sorting method of the result set. If you do not specify the sorting method, the order of the results is not guaranteed. The Response is shown below: ```sql +---------------------+-----------+----------------------------------------+ | ts | host | AVG(monitor.cpu) RANGE 10s FILL LINEAR | +---------------------+-----------+----------------------------------------+ | 2023-12-13 02:05:35 | 127.0.0.1 | 0.5 | | 2023-12-13 02:05:40 | 127.0.0.1 | 0.5 | | 2023-12-13 02:05:45 | 127.0.0.1 | 0.4 | | 2023-12-13 02:05:50 | 127.0.0.1 | 0.4 | | 2023-12-13 02:05:35 | 127.0.0.2 | 0.3 | | 2023-12-13 02:05:40 | 127.0.0.2 | 0.3 | | 2023-12-13 02:05:45 | 127.0.0.2 | 0.2 | | 2023-12-13 02:05:50 | 127.0.0.2 | 0.2 | +---------------------+-----------+----------------------------------------+ ``` ### Time range window The origin time range window steps forward and backward in the time series to generate all time range windows. In the example above, the origin alignment time is set to `2023-12-01T00:00:00`, which is also the start time of the origin time window. The `RANGE` option, along with the origin alignment time, defines the origin time range window that starts from `origin alignment timestamp` and ends at `origin alignment timestamp + range`. The `ALIGN` option defines the query resolution steps. It determines the calculation steps from the origin time window to other time windows. For example, with the origin alignment time `2023-12-01T00:00:00` and `ALIGN '5s'`, the alignment times are `2023-11-30T23:59:55`, `2023-12-01T00:00:00`, `2023-12-01T00:00:05`, `2023-12-01T00:00:10`, and so on. These time windows are left-closed and right-open intervals that satisfy the condition `[alignment timestamp, alignment timestamp + range)`. The following images can help you understand the time range window more visually: When the query resolution is greater than the time range window, the metrics data will be calculated for only one time range window. ![align > range](/align_greater_than_range.png) When the query resolution is less than the time range window, the metrics data will be calculated for multiple time range windows. ![align < range](/align_less_than_range.png) ### Align to specific timestamp The alignment times default based on the time zone of the current SQL client session. You can change the origin alignment time to any timestamp you want. For example, use `NOW` to align to the current time: ```sql SELECT ts, host, avg(cpu) RANGE '1w' FROM monitor ALIGN '1d' TO NOW BY (host); ``` Or use a `ISO 8601` timestamp to align to a specified time: ```sql SELECT ts, host, avg(cpu) RANGE '1w' FROM monitor ALIGN '1d' TO '2023-12-01T00:00:00+08:00' BY (host); ``` ### Fill null values The `FILL` option can be used to fill null values in the data. In the above example, the `LINEAR` method is used to fill null values. Other methods are also supported, such as `PREV` and a constant value `X`. For more information, please refer to the [FILL OPTION](/reference/sql/range.md#fill-option). ### Syntax Please refer to [Range Query](/reference/sql/range.md) for more information. ## Table name constraints If your table name contains special characters or uppercase letters, you must enclose the table name in backquotes. For examples, please refer to the [Table name constraints](/user-guide/deployments-administration/manage-data/basic-table-operations.md#table-name-constraints) section in the table creation documentation. ## HTTP API Use POST method to query data: ```shell curl -X POST \ -H 'authorization: Basic ' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'sql=select * from monitor' \ http://localhost:4000/v1/sql?db=public ``` The result is shown below: ```json { "code": 0, "output": [ { "records": { "schema": { "column_schemas": [ { "name": "host", "data_type": "String" }, { "name": "ts", "data_type": "TimestampMillisecond" }, { "name": "cpu", "data_type": "Float64" }, { "name": "memory", "data_type": "Float64" } ] }, "rows": [ ["127.0.0.1", 1667446797450, 0.1, 0.4], ["127.0.0.1", 1667446798450, 0.5, 0.2], ["127.0.0.2", 1667446798450, 0.2, 0.3] ] } } ], "execution_time_ms": 0 } ``` For more information about SQL HTTP request, please refer to [API document](/user-guide/protocols/http.md#post-sql-statements). --- ## View In SQL, a view is a virtual table based on the result set of an SQL statement. It contains rows and columns just like a real table. The query of a view is run every time the view is referenced in a query. In the following situations, you can use views: * Simplifying complex queries, avoiding the need to repeatedly write and send complex statements for every query. * Restricting a user to a subset of columns and rows, when combined with a permission model that can grant the view without granting its base tables. :::note A view is not a permission model on its own. The [static user provider](/user-guide/deployments-administration/authentication/static.md) grants privileges per user rather than per object, so a view alone isolates nothing there. GreptimeDB Enterprise [matches its ACLs against the object names that appear in the query](/enterprise/user.md#access-control-lists-acls), which makes it possible to grant a view without granting the tables it reads, confining the user to the columns and rows the view selects. ::: A view is created with the `CREATE VIEW` statement. ## View examples ```sql CREATE VIEW cpu_monitor AS SELECT cpu, host, ts FROM monitor; ``` The view name is `cpu_monitor`, and the query statement after `AS` is the SQL statement to present the data. Query the view: ```sql SELECT * FROM cpu_monitor; ``` ```sql +------+-----------+---------------------+ | cpu | host | ts | +------+-----------+---------------------+ | 0.5 | 127.0.0.1 | 2023-12-13 02:05:41 | | 0.3 | 127.0.0.1 | 2023-12-13 02:05:46 | | 0.4 | 127.0.0.1 | 2023-12-13 02:05:51 | | 0.3 | 127.0.0.2 | 2023-12-13 02:05:41 | | 0.2 | 127.0.0.2 | 2023-12-13 02:05:46 | | 0.2 | 127.0.0.2 | 2023-12-13 02:05:51 | +------+-----------+---------------------+ ``` Query view by `WHERE`: ```sql SELECT * FROM cpu_monitor WHERE host = '127.0.0.2'; ``` ```sql +------+-----------+---------------------+ | cpu | host | ts | +------+-----------+---------------------+ | 0.3 | 127.0.0.2 | 2023-12-13 02:05:41 | | 0.2 | 127.0.0.2 | 2023-12-13 02:05:46 | | 0.2 | 127.0.0.2 | 2023-12-13 02:05:51 | +------+-----------+---------------------+ ``` Create a view that queries data from two tables: ```sql CREATE VIEW app_cpu_monitor AS SELECT cpu, latency, host, ts FROM monitor LEFT JOIN app_monitor ON monitor.host = app_monitor.host AND monitor.ts = app_monitor.ts ``` Then query the view as if the data were coming from one single table: ```sql SELECT * FROM app_cpu_monitor WHERE host = 'host1' ``` ## Update View `CREATE OR REPLACE VIEW` to update a view, if it doesn't exist, it will be created: ```sql CREATE OR REPLACE VIEW memory_monitor AS SELECT memory, host, ts FROM monitor; ``` ## Shows the view definition Shows the `CREATE VIEW` statement that creates the named view by `SHOW CREATE VIEW view_name`: ```sql SHOW CREATE VIEW cpu_monitor; ``` ```sql +-------------+--------------------------------------------------------------+ | View | Create View | +-------------+--------------------------------------------------------------+ | cpu_monitor | CREATE VIEW cpu_monitor AS SELECT cpu, host, ts FROM monitor | +-------------+--------------------------------------------------------------+ ``` ## List Views `SHOW VIEWS` statement to find all the views: ```sql > SHOW VIEWS; +----------------+ | Views | +----------------+ | cpu_monitor | | memory_monitor | +----------------+ ``` of course, just like `SHOW TABLES`, it supports `LIKE` and `WHERE`: ```sql > SHOW VIEWS like 'cpu%'; +-------------+ | Views | +-------------+ | cpu_monitor | +-------------+ 1 row in set (0.02 sec) > SHOW VIEWS WHERE Views = 'memory_monitor'; +----------------+ | Views | +----------------+ | memory_monitor | +----------------+ ``` ## Drop View Use `DROP VIEW` statement to drop a view: ```sql DROP VIEW cpu_monitor; ``` To be quiet if it does not exist: ```sql DROP VIEW IF EXISTS test; ``` --- ## Time Zone You can specify the time zone in the client session to manage time data conveniently. The specified time zone in the client session does not affect the time data stored in the GreptimeDB server, it only applies when the client sends a request to the server. GreptimeDB converts the time value from a string representation to a datetime according to the specified time zone during ingestion or querying, or converts it back. ## Specify time zone in clients By default, all clients use [the default time zone configuration](/user-guide/deployments-administration/configuration.md#default-time-zone-configuration), which is UTC. You can also specify a time zone in each client session, which will override the default time zone configuration. ### MySQL client - **Command Line**: For configuring the time zone via the MySQL command line client, please refer to the [time zone section](/user-guide/protocols/mysql.md#time-zone) in the MySQL protocol documentation. - **MySQL Driver**: If you are using MySQL Driver in Java or Go, see the [time zone section](/reference/sql-tools.md#time-zone) of the SQL tools documentation. ### PostgreSQL client To configure the time zone for the PostgreSQL client, please refer to the [time zone section](/user-guide/protocols/postgresql.md#time-zone) in the PostgreSQL protocol documentation. ### HTTP API When using the HTTP API, you can specify the time zone through the header parameter. For more information, please refer to the [HTTP API documentation](/user-guide/protocols/http.md#time-zone). ### Dashboard The dashboard will use the local timezone as the default timezone value. You can change it in the settings menu. ### Other clients For other clients, you can change [the default time zone configuration](/user-guide/deployments-administration/configuration.md#default-time-zone-configuration) of GreptimeDB. ## Impact of time zone on SQL statements The client's time zone setting influences both data ingestion and querying. ### Ingest data The time zone set in the client impacts the data during ingestion. For more information, please refer to the [ingest data section](/user-guide/ingest-data/for-iot/sql.md#time-zone). Additionally, the default timestamp values in the table schema are influenced by the client's time zone in the same manner as the ingested data. ### Query data The time zone setting also affects data during querying. For detailed information, please refer to the [query data section](/user-guide/query-data/sql.md#time-zone). --- ## Trace Data Modeling :::warning This section currently in the experimental stage and may be adjusted in future versions. ::: In this section, we will cover how trace data is modeled in GreptimeDB as tables. We reuse the concept of Pipeline for trace data modeling. However, note that at the moment, only built-in pipelines are supported. ## Data Model Versioning First, the data types and features in GreptimeDB are evolving. For forward-compatibility, we use the pipeline name for data model versioning. Currently we have the following built-in pipeline for trace: - `greptime_trace_v1`: The recommended data model for trace tables. Specify the data model in OpenTelemetry OTLP/HTTP requests with the `x-greptime-pipeline-name` header. Use `x-greptime-pipeline-name: greptime_trace_v1` for new trace tables. We may introduce new data models by adding new available pipeline names. Note that a new pipeline may not be compatible with previous ones, so you are recommended to use it in a new table. ## Data Model The `greptime_trace_v1` data model is pretty straight-forward. By default, trace data is stored in a table named `opentelemetry_traces`. You can customize the table name by specifying the `x-greptime-trace-table-name` header in your OTLP/HTTP requests. - It maps most common data fields from [OpenTelemetry Trace](https://opentelemetry.io/docs/concepts/signals/traces/) data model to GreptimeDB's table columns. - `service_name` is extracted from `resource_attributes["service.name"]` and used as a **Tag** (part of the **Primary Key**). - `timestamp` is the start time of the span and is used as the **Time Index**. - A new `duration_nano` column is generated using `end_time - start_time`. - All attributes fields are flattened into columns using the name pattern: `[span|resource|scope]_attributes.[attribute_key]`. - Note: `resource_attributes.service.name` is excluded from flattening as it is already stored in the `service_name` column. - If the attribute value is a compound type like `Array` or `Kvlist`, it is serialized to `JSON` type of GreptimeDB. - Compound fields, `span_links` and `span_events` are stored as `JSON` type. The table will be automatically generated when your first data item arrived. It also follows our schema-less principle to update the table schema automatically for new columns, for example, the new attribute fields. For `greptime_trace_v1`, GreptimeDB also reconciles attribute column types while the schema evolves. When a trace table already exists, the existing table schema is authoritative for compatible incoming values. Scalar values can be coerced to an existing compatible column type, and existing `Int64` attribute columns may be widened to `Float64` when incoming values contain both integers and floats. If a span still cannot be written, GreptimeDB may reject that span while accepting the other spans in the request. A typical table structure generated from OpenTelemetry django instrument is like: ``` timestamp | 2025-05-07 10:03:29.657544 timestamp_end | 2025-05-07 10:03:29.661714 duration_nano | 4169970 trace_id | fb60d19aa36fdcb7d14a71ca0b9b42ae span_id | 49806a2671f2ddcb span_kind | SPAN_KIND_SERVER span_name | POST todos/ span_status_code | STATUS_CODE_UNSET span_status_message | trace_state | scope_name | opentelemetry.instrumentation.django scope_version | 0.51b0 service_name | myproject span_attributes.http.request.method | POST span_attributes.url.full | span_attributes.server.address | django:8000 span_attributes.network.peer.address | span_attributes.server.port | 8000 span_attributes.network.peer.port | span_attributes.http.response.status_code | 201 span_attributes.network.protocol.version | 1.1 resource_attributes.telemetry.sdk.language | python resource_attributes.telemetry.sdk.name | opentelemetry resource_attributes.telemetry.sdk.version | 1.30.0 span_events | [] span_links | [] parent_span_id | eccc18b6fc210f31 span_attributes.db.system | span_attributes.db.name | span_attributes.db.statement | span_attributes.url.scheme | http span_attributes.url.path | /todos/ span_attributes.client.address | 10.89.0.5 span_attributes.client.port | 44302 span_attributes.user_agent.original | python-requests/2.32.3 span_attributes.http.route | todos/ ``` To check the table definition, you can use `show create table opentelemetry_traces` statement. An output like this is expected: ``` Table | opentelemetry_traces Create Table | CREATE TABLE IF NOT EXISTS "opentelemetry_traces" ( + | "timestamp" TIMESTAMP(9) NOT NULL, + | "timestamp_end" TIMESTAMP(9) NULL, + | "duration_nano" BIGINT UNSIGNED NULL, + | "trace_id" STRING NULL SKIPPING INDEX WITH(granularity = '10240', type = 'BLOOM'), + | "span_id" STRING NULL, + | "span_kind" STRING NULL, + | "span_name" STRING NULL, + | "span_status_code" STRING NULL, + | "span_status_message" STRING NULL, + | "trace_state" STRING NULL, + | "scope_name" STRING NULL, + | "scope_version" STRING NULL, + | "service_name" STRING NULL SKIPPING INDEX WITH(granularity = '10240', type = 'BLOOM'),+ | "span_attributes.http.request.method" STRING NULL, + | "span_attributes.url.full" STRING NULL, + | "span_attributes.server.address" STRING NULL, + | "span_attributes.network.peer.address" STRING NULL, + | "span_attributes.server.port" BIGINT NULL, + | "span_attributes.network.peer.port" BIGINT NULL, + | "span_attributes.http.response.status_code" BIGINT NULL, + | "span_attributes.network.protocol.version" STRING NULL, + | "resource_attributes.telemetry.sdk.language" STRING NULL, + | "resource_attributes.telemetry.sdk.name" STRING NULL, + | "resource_attributes.telemetry.sdk.version" STRING NULL, + | "span_events" JSON NULL, + | "span_links" JSON NULL, + | "parent_span_id" STRING NULL SKIPPING INDEX WITH(granularity = '10240', type = 'BLOOM'),+ | "span_attributes.db.system" STRING NULL, + | "span_attributes.db.name" STRING NULL, + | "span_attributes.db.statement" STRING NULL, + | "span_attributes.url.scheme" STRING NULL, + | "span_attributes.url.path" STRING NULL, + | "span_attributes.client.address" STRING NULL, + | "span_attributes.client.port" BIGINT NULL, + | "span_attributes.user_agent.original" STRING NULL, + | "span_attributes.http.route" STRING NULL, + | TIME INDEX ("timestamp"), + | PRIMARY KEY ("service_name") + | ) + | PARTITION ON COLUMNS ("trace_id") ( + | trace_id < '1', + | trace_id >= 'f', + | trace_id >= '1' AND trace_id < '2', + | trace_id >= '2' AND trace_id < '3', + | trace_id >= '3' AND trace_id < '4', + | trace_id >= '4' AND trace_id < '5', + | trace_id >= '5' AND trace_id < '6', + | trace_id >= '6' AND trace_id < '7', + | trace_id >= '7' AND trace_id < '8', + | trace_id >= '8' AND trace_id < '9', + | trace_id >= '9' AND trace_id < 'a', + | trace_id >= 'a' AND trace_id < 'b', + | trace_id >= 'b' AND trace_id < 'c', + | trace_id >= 'c' AND trace_id < 'd', + | trace_id >= 'd' AND trace_id < 'e', + | trace_id >= 'e' AND trace_id < 'f' + | ) + | ENGINE=mito + | WITH( + | append_mode = 'true', + | table_data_model = 'greptime_trace_v1' + | ) ``` ### Partition Rules We included default [partition rules](/user-guide/deployments-administration/manage-data/table-sharding.md#partition) for trace table on the `trace_id` column based on the first character of it. This is optimised for retrieve trace spans by the trace id. The partition rule introduces 16 partitions for the table, a reasonable starting point for a 3-5 datanode setup. Ingestion rate, query concurrency, and total region count across the cluster may call for a different partition count. To customize the partition rule, you can: 1. Create a new table ahead-of-time by copying the DDL output by `show create table` on original table, update the `PARTITION ON` section to include your own rules. 2. Use `x-greptime-hints` [HTTP header](/user-guide/protocols/http#hints) in your OTLP ingestion request, include a hint `trace_table_partitions=n` where `n` is the partition number. Set `n` to `0` or `1` to disable partitioning. Any other value must be a power of two between `2` and `65536`; the table creation fails otherwise. ### Index We include [skipping index](/user-guide/manage-data/data-index.md#skipping-index) on `service_name`, `trace_id`, and `parent_span_id` for most typical queries. In real-world, you may want to speed up queries on other fields like an attribute field. It's possible by apply additional index on these fields using [alter table](/reference/sql/alter.md#create-an-index-for-a-column) statement. Unlike partition rules, index can be created on existing table and be affective on new data. ### Partial Success When a trace request contains multiple spans, GreptimeDB may accept the spans that can be written and reject only the spans that fail deterministic validation, such as incompatible schema or values. In this case the OTLP response contains a `partial_success` section with `rejected_spans` and `error_message`. If all spans are rejected, GreptimeDB returns `400 Bad Request`. ### Append-only Mode By default, trace table created by OpenTelemetry API are in [append only mode](/user-guide/deployments-administration/performance-tuning/design-table.md#when-to-use-append-only-tables). ### TTL You can apply [TTL on trace table](/reference/sql/alter.md#alter-table-options). ## Auxiliary Tables When you ingest trace data, GreptimeDB automatically creates two auxiliary tables to facilitate searching for services and operations. These tables are named by appending `_services` and `_operations` to your main trace table name. By default, these are named `opentelemetry_traces_services` and `opentelemetry_traces_operations`. If you customize the main trace table name using the `x-greptime-trace-table-name` HTTP header, the auxiliary tables will be named accordingly (e.g., `_services` and `_operations`). ### Services Table (`opentelemetry_traces_services`) This table stores the list of unique service names found in the trace data. - **Columns**: - `timestamp`: A constant timestamp (2100-01-01 00:00:00) used for all entries. - `service_name`: The name of the service (Tag). ### Operations Table (`opentelemetry_traces_operations`) This table stores the list of unique operations (service, span name, and span kind) found in the trace data. - **Columns**: - `timestamp`: A constant timestamp (2100-01-01 00:00:00) used for all entries. - `service_name`: The name of the service (Tag). - `span_name`: The name of the span (Tag). - `span_kind`: The kind of the span (Tag). --- ## Extending Trace Data :::warning This section currently in the experimental stage and may be adjusted in future versions. ::: You can also generate derived data from trace. In this chapter, we will show you some examples. ## Generate Aggregated Metrics from Trace The span contains `duration_nano` field for span processing time. In this example, we will create [Flow](/user-guide/flow-computation/overview.md) task to generate latency metrics from trace data. We will use OpenTelemetry Django instruments for source data. But the source data doesn't really matter because fields used in this example are all generic ones. ### Create Sink Table First, we create a sink table which is a materialzed view in Flow. For latency quantiles, we will use [uddsketch](https://arxiv.org/abs/2004.08604) for a quick estimation of latency at given percentile. ```sql CREATE TABLE "django_http_request_latency" ( "span_name" STRING NULL, "latency_sketch" BINARY, "time_window" TIMESTAMP time index, PRIMARY KEY ("span_name") ); ``` This table contains 3 key columns: - `span_name`: the type or name of span - `latency_sketch`: the uddsketch data structure - `time_window`: indicate the time window of current record ### Create Flow Next we create a flow task to generate uddsketch data for every 30s time window. The example filters spans by the scope name, which is optional depends on your data. Note: In this example, we use the default table name `opentelemetry_traces` as the source trace table. You should replace it with your own trace table name if it's customized. ```sql CREATE FLOW django_http_request_latency_flow SINK TO django_http_request_latency EXPIRE AFTER '30m' COMMENT 'Aggregate latency using uddsketch' AS SELECT span_name, uddsketch_state(128, 0.01, "duration_nano") AS "latency_sketch", date_bin('30 seconds'::INTERVAL, "timestamp") as "time_window", FROM opentelemetry_traces WHERE scope_name = 'opentelemetry.instrumentation.django' GROUP BY span_name, time_window; ``` ### Query Metrics The sink table will be filled with aggregated data as trace data ingested. We can use following SQL to get p90 latency of the each span. ```sql SELECT span_name, time_window, uddsketch_calc(0.90, "latency_sketch") AS p90 FROM django_http_request_latency ORDER BY time_window DESC LIMIT 20; ``` The query will return results like: ``` span_name | time_window | p90 ---------------------+----------------------------+-------------------- GET todos/ | 2025-05-09 02:38:00.000000 | 4034758.586053441 POST todos/ | 2025-05-09 02:38:00.000000 | 22988738.680499777 PUT todos// | 2025-05-09 02:38:00.000000 | 5338559.200101535 GET todos/ | 2025-05-09 02:37:30.000000 | 4199425.807196321 POST todos/ | 2025-05-09 02:37:30.000000 | 15104466.164886404 PUT todos// | 2025-05-09 02:37:30.000000 | 16693072.385310777 GET todos/ | 2025-05-09 02:37:00.000000 | 4370813.453648573 POST todos/ | 2025-05-09 02:37:00.000000 | 30417369.407361753 PUT todos// | 2025-05-09 02:37:00.000000 | 14512192.224492861 GET todos/ | 2025-05-09 02:36:30.000000 | 3578495.53232116 POST todos/ | 2025-05-09 02:36:30.000000 | 15409606.895490168 PUT todos// | 2025-05-09 02:36:30.000000 | 15409606.895490168 GET todos/ | 2025-05-09 02:36:00.000000 | 3507634.2346514342 POST todos/ | 2025-05-09 02:36:00.000000 | 45377987.991290994 PUT todos// | 2025-05-09 02:36:00.000000 | 14512192.224492861 GET todos/ | 2025-05-09 02:35:30.000000 | 3237945.86410019 POST todos/ | 2025-05-09 02:35:30.000000 | 15409606.895490168 PUT todos// | 2025-05-09 02:35:30.000000 | 13131130.769316385 GET todos/ | 2025-05-09 02:35:00.000000 | 3173828.124217018 POST todos/ | 2025-05-09 02:35:00.000000 | 14512192.224492861 (20 rows) ``` --- ## Traces :::warning This section currently in the experimental stage and may be adjusted in future versions. ::: Native support for Trace data is introduced since GreptimeDB v0.14. In this section we will cover from basic ingestion and query steps, to advanced internals of data modeling. - [Ingestion and Query](./read-write.md) - [Trace Data Modeling in GreptimeDB](./data-model.md) - [Extending Trace Data](./extend-trace.md) ## Query Traces - [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md#traces-query): Search traces and inspect span records in the built-in Dashboard using a builder or code editor. - [Ingestion and Query](./read-write.md#query): Jaeger-compatible API and SQL query interfaces. --- ## Ingestion and Query :::warning This section currently in the experimental stage and may be adjusted in future versions. ::: In this section, we will get started with trace data in GreptimeDB from ingestion and query. GreptimeDB doesn't invent new protocols for trace, it follows existing standard and widely adopted protocols. ## Ingestion GreptimeDB uses OpenTelemetry OTLP/HTTP protocol as the primary trace data ingestion protocol. OpenTelemetry Trace is the most adopted subprotocol in OpenTelemetry family. ### OpenTelemetry SDK If OpenTelemetry SDK/API is used in your application, you can configure an OTLP/HTTP exporter to write trace data directly to GreptimeDB. We covered this part in our [OpenTelemetry protocol docs](/user-guide/ingest-data/for-observability/opentelemetry.md). You can follow the guide for endpoint and parameters. ### OpenTelemetry Collector [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) is a vendor-neutral service for collecting and processing OpenTelemetry data. You can also configure it to send trace data to GreptimeDB using OTLP/HTTP. #### Start OpenTelemetry Collector You can use the following command to quickly start an OpenTelemetry Collector instance, which will listen on ports `4317` (gRPC) and `4318` (HTTP): ```shell docker run --rm \ --network host \ -p 4317:4317 \ -p 4318:4318 \ -v $(pwd)/config.yaml:/etc/otelcol-contrib/config.yaml \ otel/opentelemetry-collector-contrib:0.159.0 ``` The content of the `config.yaml` file is as follows: ```yaml receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 exporters: otlp_http: endpoint: "http://greptimedb:4000/v1/otlp" # Replace greptimedb with your setup headers: x-greptime-pipeline-name: "greptime_trace_v1" #authorization: "Basic " tls: insecure: true service: pipelines: traces: receivers: [otlp] exporters: [otlp_http] ``` #### Write Trace Data to OpenTelemetry Collector You can configure the corresponding exporter to write traces data to the OpenTelemetry Collector. For example, you can use the environment variable `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` to configure the endpoint of the exporter: ```shell export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://localhost:4318/v1/traces" ``` For convenience, you can use the tool [`telemetrygen`](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/cmd/telemetrygen) to quickly generate traces data and write it to the OpenTelemetry Collector. For more details, please refer to the [OpenTelemetry Collector official documentation](https://opentelemetry.io/docs/collector/quick-start/). You can use the following command to install `telemetrygen` (please ensure you have installed Go): ```shell go install github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen@latest ``` Then you can use the following command to generate traces data and write it to the OpenTelemetry Collector: ```shell telemetrygen traces --otlp-insecure --traces 3 ``` The above command will generate 3 traces data and write it to the OpenTelemetry Collector via gRPC protocol, and eventually stored into GreptimeDB. ### Authorization The GreptimeDB OTEL endpoint supports Basic authentication. For details, please refer to the [authentication](/user-guide/protocols/http.md#authentication) documentation. ### GreptimeDB Pipeline The HTTP header `x-greptime-pipeline-name` is required for ingesting trace data. Here we reuse the Pipeline concept of GreptimeDB for data transformation. Use the built-in `greptime_trace_v1` pipeline for trace data. No custom pipeline is allowed for the moment. ### Append-only Mode By default, trace table created by OpenTelemetry API are in [append only mode](/user-guide/deployments-administration/performance-tuning/design-table.md#when-to-use-append-only-tables). ## Query To query the trace data, GreptimeDB has two types of API provided. The Jaeger compatible API and GreptimeDB's original SQL based query interfaces, which is available in HTTP, MySQL and Postgres protocols. ### Jaeger We build Jaeger compatibility layer into GreptimeDB so you can reuse your Jaeger frontend or any other integrations like Grafana's Jaeger data source. For detail of Jaeger's endpoint and parameters, check [our Jaeger protocol docs](/user-guide/query-data/jaeger.md). ### SQL All data in GreptimeDB is available for query using SQL, via MySQL and other transport protocol. By default, trace data is written into the table called `opentelemetry_traces`. The table name is customizable via header `x-greptime-trace-table-name`. You can run SQL queries against the table: ```sql SELECT * FROM public.opentelemetry_traces \G ``` An example output is like ``` *************************** 1. row *************************** timestamp: 2025-05-07 10:03:29.657544 timestamp_end: 2025-05-07 10:03:29.661714 duration_nano: 4169970 parent_span_id: eccc18b6fc210f31 trace_id: fb60d19aa36fdcb7d14a71ca0b9b42ae span_id: 49806a2671f2ddcb span_kind: SPAN_KIND_SERVER span_name: POST todos/ span_status_code: STATUS_CODE_UNSET span_status_message: trace_state: scope_name: opentelemetry.instrumentation.django scope_version: 0.51b0 service_name: myproject span_attributes.http.request.method: POST span_attributes.url.full: span_events: [] span_links: [] ... ``` We will cover more information about the table structure in [Data Model](./data-model.md) section. --- ## Vector Data Type ## Overview In the field of artificial intelligence, vectors are an important data type used to represent features or samples within a dataset. Vectors are utilized in various machine learning and deep learning algorithms, such as recommendation systems, natural language processing, and image recognition. By introducing the vector type, GreptimeDB enables more efficient support for these AI applications, offering robust vector storage and computation capabilities. ## Basic Introduction to Vector Type In GreptimeDB, a vector is represented as an array of Float32 (32-bit floating-point) with a fixed dimension. When creating a vector type column, the dimension must be specified and cannot be changed afterward. ## Defining a Vector Type Column In SQL, a table with a vector type column can be defined using the following syntax. Note that the dimension `` must be a positive integer that specifies the length of the vector. ```sql CREATE TABLE ( ... VECTOR() ); ``` For example, to define a table with a vector type column of dimension 3: ```sql CREATE TABLE vecs ( ts TIMESTAMP TIME INDEX, vec_col VECTOR(3) ); ``` ## Inserting Vector Data In GreptimeDB, you can insert vector data into the database in several ways. The simplest method is to use a string format and implicitly convert it to a vector. The string needs to be enclosed in square brackets `[]`. Below is an example of using implicit conversion in SQL: ```sql INSERT INTO () VALUES ('[, , ...]'), ('[, , ...]'), ... ('[, , ...]'); ``` For example, to insert three 3-dimensional vectors: ```sql INSERT INTO vecs (ts, vec_col) VALUES ('2024-11-18 00:00:01', '[1.0, 2.0, 3.0]'), ('2024-11-18 00:00:02', '[4.0, 5.0, 6.0]'), ('2024-11-18 00:00:03', '[7.0, 8.0, 9.0]'); ``` If you wish to have more explicit control over data conversion, you can use the `parse_vec` function to explicitly parse a string into a vector: ```sql INSERT INTO vecs (ts, vec_col) VALUES ('2024-11-18 00:00:01', parse_vec('[1.0, 2.0, 3.0]')), ('2024-11-18 00:00:02', parse_vec('[4.0, 5.0, 6.0]')), ('2024-11-18 00:00:03', parse_vec('[7.0, 8.0, 9.0]')); ``` ## Vector Calculations GreptimeDB supports various vector functions for calculating the similarity between vectors, including `vec_l2sq_distance`, `vec_cos_distance`, and `vec_dot_product`. These functions are used in AI applications to search for the most similar content. To perform vector calculations, use the following SQL format: ```sql SELECT (, ) FROM ; ``` For example, to find the vector with the smallest squared Euclidean distance to a given vector `[5.0, 5.0, 5.0]` and display the distance, you can use the following query: ```sql SELECT vec_to_string(vec_col), vec_l2sq_distance(vec_col, '[5.0, 5.0, 5.0]') AS distance FROM vecs ORDER BY distance; ``` Upon executing this query, you will get results similar to the following: ``` +-----------------------------+----------+ | vec_to_string(vecs.vec_col) | distance | +-----------------------------+----------+ | [4,5,6] | 2 | | [1,2,3] | 29 | | [7,8,9] | 29 | +-----------------------------+----------+ 3 rows in set (0.01 sec) ``` Through this approach, GreptimeDB enables you to quickly identify and locate similar data vectors, thus providing robust support for AI applications. For more information about vector functions, please refer to the [Vector Functions Reference](/reference/sql/functions/vector.md). --- ## Auto Repartition Auto Repartition is an Autopilot strategy that automatically splits large Regions into smaller Regions. When a table has a large Region that may become a performance bottleneck, Auto Repartition samples data, generates new partition boundaries, and submits a Repartition action. The split Regions can then be scheduled across multiple Datanodes to distribute potential bottlenecks. Auto Repartition reduces the operational cost of manually identifying large Regions and running Repartition. For manual Repartition, see [Repartition](/user-guide/deployments-administration/manage-data/repartition.md). ## Prerequisites :::warning WARNING Auto Repartition depends on GreptimeDB Repartition. It is only available in distributed clusters and requires: - [Shared object storage](/user-guide/deployments-administration/configuration.md#storage-options), such as AWS S3. - [GC](/user-guide/deployments-administration/manage-data/gc.md) enabled on Metasrv and all Datanodes. Otherwise, Repartition cannot be executed. ::: Object storage stores Region files. GC reclaims old files after their references are released, which prevents files still in use from being deleted during Repartition. ## When to use Auto Repartition Auto Repartition is useful in the following scenarios: - Some large Regions may become performance bottlenecks. - The original partition rule no longer matches the current data distribution. - You want to split large Regions into smaller Regions and distribute potential bottlenecks through later scheduling. - You want to reduce the operational cost of manually identifying large Regions and running Repartition. ## Auto Repartition for unpartitioned tables Auto Repartition can also work with unpartitioned tables when a repartition column hint is specified. For an unpartitioned table, GreptimeDB Enterprise does not infer partition columns automatically. You can specify the preferred column for future Auto Repartition when creating the table: ```sql CREATE TABLE sensor_readings ( host STRING, cpu DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY(host) ) WITH ('repartition.column.hint'='host'); ``` You can also set or update the hint later by using `ALTER TABLE`: ```sql ALTER TABLE sensor_readings SET 'repartition.column.hint'='host'; ``` To remove the hint: ```sql ALTER TABLE sensor_readings UNSET 'repartition.column.hint'; ``` The hint only records metadata for future Auto Repartition. It does not trigger Repartition immediately. After the table meets the Auto Repartition trigger conditions, GreptimeDB Enterprise can use the hinted column to generate partition boundaries and submit a Repartition action. The repartition column hint has the following restrictions: - It must specify exactly one column. - The specified column must exist in the table. - The specified column cannot be the time index column. - It can only be set on a table without partition metadata. - When using `ALTER TABLE`, it must be set or unset separately from other table options. ## Limitations Auto Repartition works for partitioned tables and unpartitioned tables with `repartition.column.hint`. For unpartitioned tables, GreptimeDB Enterprise does not infer partition columns automatically. For more information about table partitioning and Repartition, see [Table Sharding](/user-guide/deployments-administration/manage-data/table-sharding.md) and [Repartition](/user-guide/deployments-administration/manage-data/repartition.md). ## Configuration Auto Repartition depends on the Autopilot runtime and cluster statistics. The following example includes both shared configuration and Auto Repartition configuration: ```toml [[plugins]] [plugins.autopilot] tick_interval = "45s" [[plugins]] [plugins.cluster_stat] sampling_window = "45s" max_history_windows = 5 ewma_alpha = 0.2 [[plugins]] [plugins.auto_repartition] split_trigger_ratio = 1.8 max_split_parts = 3 table_repartition_cooldown_period = "60s" max_actions_per_tick = 4 max_actions_per_table_per_tick = 2 ``` In this example: - `plugins.autopilot` controls the Autopilot scheduling interval. - `plugins.cluster_stat` controls sampling and smoothing for Datanode and Region write statistics. - `plugins.auto_repartition` controls large Region split trigger conditions, split size, and submission limits. For details about shared configuration, see [Autopilot configuration](./overview.md#configuration). ## Core options | Option | Default | Description | | --- | --- | --- | | `split_trigger_ratio` | `1.8` | The load ratio required before a Region is considered for splitting. For example, the default value `1.8` means split planning starts only when a Region reaches more than 1.8 times the target per-Region write load. | | `max_split_parts` | `3` | The maximum number of child Regions a single Region can be split into. | | `table_repartition_cooldown_period` | `"60s"` | The table-level Repartition cooldown period. After a Repartition request is submitted successfully, the same table will not submit another Repartition request during this period. | | `max_actions_per_tick` | `4` | The maximum number of Repartition actions submitted in one scheduling cycle. | | `max_actions_per_table_per_tick` | `2` | The maximum number of Repartition actions submitted for one table in one scheduling cycle. | ## Advanced options The following options usually do not need to be changed. Adjust them only when you understand the table distribution and split-point selection behavior. | Option | Default | Description | | --- | --- | --- | | `sampling_budget` | `"10MB"` | The maximum amount of data sampled when computing split points for one Region. A larger budget may improve split-point quality but increases planning cost. | | `split_segment_min_ratio` | `0.7` | The minimum allowed segment-size ratio when validating a split recommendation. | | `split_segment_max_ratio` | `1.3` | The maximum allowed segment-size ratio when validating a split recommendation. | | `min_samples` | `3` | The minimum number of historical samples required to evaluate Region write stability. | | `max_region_history_cv` | `0.2` | The maximum coefficient of variation allowed for Region write history. Regions above this value are considered unstable. | --- ## Overview Autopilot is a GreptimeDB Enterprise capability that automatically optimizes cluster load and data distribution. It runs in Metasrv, continuously collects write throughput and query CPU time statistics from Datanodes and Regions, and submits scheduling actions when the configured conditions are met. This reduces the operational cost of identifying hotspots and manually adjusting the cluster. Autopilot currently includes the following capabilities: - **Region Balancer**: Automatically migrates hot Regions to balance write load and read query CPU load across Datanodes. - **Auto Repartition**: Automatically splits large Regions into smaller Regions to prevent a single large Region from becoming a performance bottleneck. The split Regions can then be scheduled across multiple Datanodes to distribute potential bottlenecks. ## How it works Autopilot consists of a shared runtime, shared cluster statistics, and scheduling strategies: - **Runtime**: Triggers a scheduling cycle at a fixed interval. - **Cluster statistics**: Collects Region write throughput and query CPU time statistics from Datanode heartbeats and smooths short-term fluctuations. - **Scheduling strategies**: Decide whether to move Regions or split large Regions based on the collected statistics. - **Executors**: Submit actions generated by the strategies, such as Region Migration or Repartition. When both Region Balancer and Auto Repartition are enabled, they share the same Autopilot runtime and cluster statistics. ## When to use Autopilot Autopilot is useful in the following scenarios: - Some Datanodes have write load or read query CPU load that remains significantly higher than others. - Some large Regions may become performance bottlenecks. - You want to reduce the operational cost of manually identifying load bottlenecks and running Region Migration or Repartition. ## Limitations Different Autopilot strategies have their own limitations: - Region Balancer requires the number of schedulable Regions to be greater than the number of active Datanodes. Otherwise, moving Regions cannot make the load evenly distributed across Datanodes. - Auto Repartition works for partitioned tables and unpartitioned tables with `repartition.column.hint`. For unpartitioned tables, GreptimeDB Enterprise does not infer partition columns automatically. For more information about table partitioning and Repartition, see [Table Sharding](/user-guide/deployments-administration/manage-data/table-sharding.md) and [Repartition](/user-guide/deployments-administration/manage-data/repartition.md). ## Configuration Autopilot configuration includes shared configuration and strategy-specific configuration: - `plugins.autopilot`: Configures the Autopilot runtime. - `plugins.cluster_stat`: Configures sampling and smoothing for Datanode and Region write throughput and query CPU time statistics. - `plugins.region_balancer`: Enables and configures Region Balancer. - `plugins.auto_repartition`: Enables and configures Auto Repartition. The following example enables both Region Balancer and Auto Repartition: ```toml [[plugins]] [plugins.autopilot] tick_interval = "45s" [[plugins]] [plugins.cluster_stat] sampling_window = "45s" max_history_windows = 5 ewma_alpha = 0.2 [[plugins]] [plugins.region_balancer] acceptable_load_ratio = 0.12 min_load_threshold = "4MB" region_migration_cooldown_period = "1h" write_window_stability_threshold = 2 [[plugins]] [plugins.auto_repartition] split_trigger_ratio = 1.8 max_split_parts = 3 table_repartition_cooldown_period = "60s" max_actions_per_tick = 4 max_actions_per_table_per_tick = 2 ``` If you only need one strategy, configure only `plugins.region_balancer` or `plugins.auto_repartition`. ## Autopilot runtime configuration | Option | Default | Description | | --- | --- | --- | | `tick_interval` | `"45s"` | The interval of Autopilot scheduling cycles. A shorter interval reacts faster to load changes but may increase scheduling overhead. | ## Cluster statistics configuration | Option | Default | Description | | --- | --- | --- | | `sampling_window` | `"45s"` | The duration of each statistics window. A larger window smooths short-term fluctuations but reacts more slowly. | | `max_history_windows` | `5` | The number of historical statistics windows to keep. Region Balancer and Auto Repartition use historical windows to determine whether load is stable. | | `ewma_alpha` | `0.2` | The EWMA smoothing factor. A larger value gives more weight to recent observations. A smaller value makes the statistics smoother. | ## Next steps - To automatically balance write load and read query CPU load across Datanodes, see [Region Balancer](./region-balancer.md). - To automatically split large Regions, see [Auto Repartition](./auto-repartition.md). --- ## Region Balancer Region Balancer is an Autopilot strategy that automatically balances Region write load and read query CPU load across Datanodes. When some Datanodes remain under high load, Region Balancer selects suitable Regions and submits Region Migration actions to move them to lower-load Datanodes. Region Balancer runs in Metasrv and depends on the shared Autopilot runtime and cluster statistics. For an overview of Autopilot, see [Autopilot](./overview.md). ## When to use Region Balancer Region Balancer is useful in the following scenarios: - Some Datanodes have a write load that remains higher than others. - You want to reduce the operational cost of manually identifying hot nodes and running Region Migration. ## Limitations Region Balancer requires the number of schedulable Regions to be greater than the number of active Datanodes. If the number of Regions is not greater than the number of active Datanodes, moving Regions cannot make the load evenly distributed across Datanodes. ## Configuration Region Balancer depends on the Autopilot runtime and cluster statistics. The following example includes both shared configuration and Region Balancer configuration: ```toml [[plugins]] [plugins.autopilot] tick_interval = "45s" [[plugins]] [plugins.cluster_stat] sampling_window = "45s" max_history_windows = 5 ewma_alpha = 0.2 [[plugins]] [plugins.region_balancer] acceptable_load_ratio = 0.12 min_load_threshold = "4MB" region_migration_cooldown_period = "1h" write_window_stability_threshold = 2 ``` In this example: - `plugins.autopilot` controls the Autopilot scheduling interval. - `plugins.cluster_stat` controls sampling and smoothing for Datanode and Region write statistics. - `plugins.region_balancer` controls Region Balancer trigger conditions, cooldown, and migration limits. For details about shared configuration, see [Autopilot configuration](./overview.md#configuration). ## Core options | Option | Default | Description | | --- | --- | --- | | `acceptable_load_ratio` | `0.12` | The load ratio threshold above the average Datanode write load. For example, the default value `0.12` means a Datanode may be considered high-load when its write load is more than 12% above the average. | | `min_load_threshold` | `"4MB"` | The minimum Datanode write load required to trigger balancing. This option represents a write rate in bytes/s. The configured value is written as a byte size. For example, `"4MB"` means 4MB/s. If the load is below this threshold, migration is not triggered even if the load is imbalanced. This avoids unnecessary scheduling under low traffic. | | `region_migration_cooldown_period` | `"1h"` | The cooldown period after a Region migration. During the cooldown period, the same Region will not be migrated again. | | `write_window_stability_threshold` | `2` | The number of historical statistics windows in which a Datanode must continuously satisfy the write-high-load condition before write-driven migration is triggered. A larger value reduces false positives caused by short-term fluctuations. | | `read_window_stability_threshold` | `4` | The number of historical statistics windows in which a Datanode must continuously satisfy the read-high-load condition before read-driven migration is triggered. A larger value reduces migrations caused by short-term read-load fluctuations. | ## Advanced options The following options usually do not need to be changed. Adjust them only when you understand the workload characteristics and scheduling behavior. Region write and read stability are evaluated independently and affect migration candidates for their respective load dimensions. | Option | Default | Description | | --- | --- | --- | | `region_min_load_threshold` | `"10KB"` | The minimum write load for a Region to be considered movable. This option represents a write rate in bytes/s. The configured value is written as a byte size. For example, `"10KB"` means 10KB/s. Regions below this threshold are not selected as migration candidates. | | `region_min_cpu_time_threshold` | `100000000.0` | The minimum query CPU time rate for a Region to be considered read-active and eligible for read-driven rebalancing. The unit is query CPU-time nanoseconds per second (ns/s); the default equals 100 ms of query CPU time per second. It also acts as the minimum total Datanode read CPU load required to classify a Datanode as read-high-load. | | `scorer_var_bound` | `0.5` | The load bound used by the scorer to evaluate migration candidates. This value must be greater than `acceptable_load_ratio`. | | `min_write_samples` | `3` | The minimum number of write-throughput history samples required to evaluate Region write stability. | | `max_write_region_history_cv` | `0.2` | The maximum acceptable coefficient of variation for a Region's write-throughput history. Regions above this value are considered write-unstable. | | `min_read_samples` | `3` | The minimum number of query CPU time history samples required to evaluate Region read stability. | | `max_read_region_history_cv` | `0.3` | The maximum acceptable coefficient of variation for a Region's query CPU time history. Regions above this value are considered read-unstable. | | `datanode_max_unstable_or_unknown_count_ratio` | `0.5` | The maximum ratio of unstable or unknown Regions on a Datanode. Datanodes above this ratio are excluded from scheduling. | | `datanode_max_unstable_or_unknown_load_ratio` | `0.5` | The maximum ratio of unstable or unknown Region load on a Datanode. Datanodes above this ratio are excluded from scheduling. | | `max_actions_per_tick` | `2` | The maximum number of migration actions submitted in one scheduling cycle. | | `max_actions_per_source_datanode` | `2` | The maximum number of Regions moved out from one source Datanode in one scheduling cycle. | | `max_actions_per_target_datanode` | `1` | The maximum number of Regions moved into one target Datanode in one scheduling cycle. | ## Legacy options Earlier versions supported configuring the following options directly under `plugins.region_balancer`: - `tick_interval` - `window_size` - `ewma_alpha` These options are still compatible, but they are not recommended for new configurations. Use the following shared options instead: - `plugins.autopilot.tick_interval` for the Autopilot scheduling interval. - `plugins.cluster_stat.sampling_window` for the statistics window. - `plugins.cluster_stat.ewma_alpha` for the EWMA smoothing factor. --- ## Continuous Profiling Continuous Profiling in the Management Console monitors GreptimeDB component resource usage and stores pprof-format memory and CPU profiles in the monitoring GreptimeDB instance. It helps operators investigate memory growth, high CPU usage, and performance regressions without manually logging in to each component. The Continuous Profiling UI is available starting from dashboard version `v0.2.0-alpha.8`. Continuous Profiling is configured on the dashboard apiserver and works at the cluster level. When enabled, the monitor scans component series from cluster metrics, captures the first seen component, and captures later profiles when resource usage grows by the configured step or reaches a new high above the configured threshold. ## Memory Profiles Enable memory profile monitoring in the dashboard apiserver configuration: ```yaml monitoring: greptimedb: url: http://monitoring-greptimedb:4000 memory_profile: enabled: true memory_profiles_database: public memory_profiles_table: _gt_memory_profiles memory_profile_step_mb: 100 high_memory_threshold_mb: 1024 ``` Memory profile configuration items: - `enabled`: Enables or disables memory profile monitoring. - `memory_profiles_database`: Database that stores captured memory profiles. The default is `public`. - `memory_profiles_table`: Table that stores captured memory profiles. The default is `_gt_memory_profiles`. - `memory_profile_step_mb`: Memory growth step, in MB, that triggers a new profile capture after the first capture. - `high_memory_threshold_mb`: High-memory threshold, in MB. When memory usage is above this value, each new memory high watermark triggers a profile capture. Set it to `0` to disable high-memory capture. The profile table defaults to `public._gt_memory_profiles` and is created with a default TTL of 30 days. The monitor uses memory usage, memory limit, and process start time metrics to decide when to capture a new profile. The target GreptimeDB process must start with jemalloc memory profiling enabled, as described in [Memory profiling](user-guide/deployments-administration/performance-tuning/performance-tuning-tips.md#memory-profiling). For command-line deployments on Linux, start the process with `MALLOC_CONF=prof:true`: ```bash MALLOC_CONF=prof:true ./greptime standalone start ``` For Helm deployments, add the environment variable to the target role, or to `base.podTemplate.main.env` to apply it to all GreptimeDB roles: ```yaml base: podTemplate: main: env: - name: MALLOC_CONF value: "prof:true" ``` The Management Console lists captured memory profiles with filters for Pod, App, Role, and time range. Use **Capture** to manually capture a profile for a component when you need an on-demand snapshot. ![Memory Profile list](/profile-page.png) The table shows when the profile was captured, the component App and Pod, the component endpoint, memory usage, usage percentage, and profile status. Use the row actions to open a flamegraph, compare the profile with a base profile, or download the raw pprof file for offline analysis. The flamegraph view renders a captured profile in the browser. The top toolbar supports common pprof interactions such as ordering, switching between **Left Heavy** and **Sandwich** views, importing and exporting profile data, and adjusting the display theme. ![Memory Profile flamegraph](/flame-ui.png) ## CPU Profiles Enable CPU profile monitoring in the dashboard apiserver configuration: ```yaml monitoring: greptimedb: url: http://monitoring-greptimedb:4000 cpu_profile: enabled: true cpu_profiles_database: public cpu_profiles_table: _gt_cpu_profiles cpu_profile_step_millicores: 100 high_cpu_threshold_millicores: 800 seconds: 5 frequency: 99 ``` CPU profile configuration items: - `enabled`: Enables or disables CPU profile monitoring. - `cpu_profiles_database`: Database that stores captured CPU profiles. The default is `public`. - `cpu_profiles_table`: Table that stores captured CPU profiles. The default is `_gt_cpu_profiles`. - `cpu_profile_step_millicores`: CPU usage growth step, in millicores, that triggers a new profile capture after the first capture. - `high_cpu_threshold_millicores`: High-CPU threshold, in millicores. When CPU usage is above this value, each new CPU high watermark triggers a profile capture. Set it to `0` to disable high-CPU capture. - `seconds`: CPU profiling duration, in seconds, for each capture. - `frequency`: CPU profiling sampling frequency, in Hz. The profile table defaults to `public._gt_cpu_profiles` and is created with a default TTL of 30 days. The monitor uses CPU usage, CPU limit, and process start time metrics to decide when to capture a new profile. The **CPU Profile** page follows the same interaction pattern as Memory Profile. Use Pod, App, Role, and time range filters to find profile records. Use **Capture** for an on-demand CPU profile, and use row actions to open the flamegraph or download the raw pprof file for offline analysis. Manual capture ignores threshold state but still requires the pod to be discoverable from the corresponding CPU metrics. --- ## Webhook Triggers Webhook triggers monitor cluster resource usage metrics and send HTTP notifications when a configured threshold is reached. This is an enterprise-only feature and is available only when the enterprise dashboard is deployed with dashboard version `v0.2.0-alpha.10` or later. Configure webhook triggers per provisioned instance under `settings.monitoring.webhook_triggers` in the dashboard apiserver configuration. Enabled webhook triggers require a metrics source, either `settings.monitoring.greptimedb.url` or `settings.monitoring.metrics.prometheus`. ```yaml provisionedInstances: - name: mycluster settings: monitoring: greptimedb: url: http://monitoring-greptimedb:4000 # Or use a Prometheus-compatible metrics source: # metrics: # prometheus: http://prometheus:9090 webhook_triggers: - name: high-datanode-memory enabled: true roles: [datanode] metric: memory_usage_percent operator: ">=" threshold: 90 cooldown_seconds: 300 url: https://alerts.example.com/datanode-memory headers: Authorization: Bearer token - name: high-frontend-cpu enabled: true roles: [frontend] metric: cpu_usage_millicores threshold: 1000 cooldown_seconds: 600 url: https://alerts.example.com/frontend-cpu ``` Webhook trigger configuration items: - `name`: Trigger name. Required when `enabled` is `true`. The name must be unique within the instance and must not contain `/`. - `enabled`: Enables or disables the trigger. - `roles`: Optional role filter. Omit it or leave it empty to match all roles. Supported roles are `frontend`, `metasrv`, `datanode`, and `flownode`. - `metric`: Resource usage metric to evaluate. Supported metrics are `memory_usage_percent`, `memory_usage_bytes`, `cpu_usage_percent`, and `cpu_usage_millicores`. - `operator`: Comparison operator. The default and only supported value is `>=`. - `threshold`: Threshold value. It must be greater than `0`. Percentage metrics must be less than or equal to `100`. - `cooldown_seconds`: Minimum interval between repeated `firing` notifications for the same active alert. The default is `300` seconds. - `url`: Webhook endpoint. Required when `enabled` is `true`; the URL must use `http://` or `https://`. - `headers`: Optional custom HTTP headers, for example `Authorization`. The webhook client always sends `Content-Type: application/json`. When a matching component crosses the threshold, the dashboard apiserver sends a `firing` payload. While the alert remains active, repeated `firing` payloads for the same instance, trigger, pod, and process start time are suppressed until `cooldown_seconds` elapses. When the metric drops below the threshold, the dashboard apiserver sends a `resolved` payload. Webhook payloads use a fixed JSON schema and cannot be templated. A representative `firing` payload looks like this: ```json { "status": "firing", "trigger_name": "high-datanode-memory", "metric": "memory_usage_percent", "operator": ">=", "threshold": 90, "value": 91.2, "instance": "ns_demo", "cluster": "demo", "namespace": "ns", "pod": "demo-datanode-0", "role": "datanode", "app": "greptime-datanode", "component_instance": "datanode-0", "endpoint": "http://demo-datanode-0:4000", "process_start_time_seconds": 1760000000, "starts_at": "2026-06-23T10:00:00Z", "ends_at": null, "sent_at": "2026-06-23T10:00:00Z" } ``` For `resolved` payloads, `status` is `resolved`, `value` is the below-threshold value that resolved the alert, and `ends_at` is set. :::note Webhook trigger state is kept in dashboard apiserver memory and is not durable. There is no durable retry queue. If the dashboard apiserver restarts, or if an instance, trigger, or pod series disappears, the corresponding state may be forgotten without sending `resolved`. Receivers that need hard guarantees should expire alerts on their side. ::: --- ## Management Console The GreptimeDB Enterprise Management Console extends the open-source [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) with enterprise operational capabilities. The sidebar is organized into two groups: | Group | Scope | | --- | --- | | **Data Management** | Query, ingest, and manage data — same features as the open-source Dashboard | | **Operation** | Cluster observability and administration — enterprise only | ## Data Management **Data Management** covers query, ingest, pipelines, Flow, and Visualization — the same features as the open-source Dashboard. See [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) for details. ## Operation **Operation** provides enterprise-only tools for cluster observability and administration. ### Overview The **Overview** page displays cluster-wide status and resource usage. - Database, table, and region counts; storage size and ingestion rate. - Cluster CPU and memory usage with utilization trends. - Per-node breakdown by role (Frontend, Datanode, Metasrv, Flownode). ![Overview](/entdashboard/overview.png) ### Metrics The **Metrics** page shows cluster operational metrics in a single built-in monitoring view, organized into **Overview**, **Ingestion**, and **Queries** sections. - Filter by node role and time range. - Covers request rates, latency, storage, and resource utilization. ![Metrics](/entdashboard/metrics.png) ### Instance Logs **Instance Logs** enables log search and analysis for GreptimeDB components. - Filter by role, instance, log level, time range, and keywords. - Export results for further analysis. ![Instance Logs](/entdashboard/logs.png) ### Slow Query **Slow Query** lists long-running SQL and PromQL queries. - View execution time and full query text. - Use **Explain Query** to analyze execution plans and optimize performance. ![Slow Query](/entdashboard/slowquery.png) ### Region Management **Region Management** provides region-level operational capabilities. - **By Datanode** and **By Table** views for region distribution and details. - **Flush** and **Compact** operations to optimize storage and performance. - **Migrate** regions between nodes with progress tracking. ![Region Management](/entdashboard/region.png) ### Instance Management **Instance Management** configures connections to GreptimeDB clusters. - Set HTTP URL, Meta URL, default database, and timezone. - Configure Kubernetes license secrets and deployment metadata. - Configure GreptimeDB and Prometheus monitoring endpoints. - **Test Connection**, **Save**, and **Update License** actions. ![Instance Management](/entdashboard/instancemanage.png) ### User Management **User Management** manages user accounts and access control in the console. - Create users and assign privileges (Read, Write, Read & Write, Custom, Admin). - Configure table-level ACLs by table or pattern. - For RBAC and ACL details, see [Built-in User Management](./user.md). ![User Management](/entdashboard/usermanage.png) ### CPU and Memory Profiling The sidebar provides **Memory Profile** and **CPU Profile** entries for continuous profiling of GreptimeDB components. For configuration and usage, see [Continuous Profiling](./console-ui/continuous-profiling.md). The enterprise dashboard also supports [Webhook Triggers](./console-ui/webhook-triggers.md) for sending notifications based on cluster resource usage metrics. --- ## LDAP Authentication In addition to the built-in [static user provider](/user-guide/deployments-administration/authentication/static.md) in GreptimeDB OSS, GreptimeDB Enterprise offers the capability to connect to an external LDAP server for authentication. ## Configuration Similar to [LDAP in PostgreSQL](https://www.postgresql.org/docs/current/auth-ldap.html), in GreptimeDB, LDAP authentication is operated in two modes: "simple bind" and "search bind", too. In the "simple bind" mode, GreptimeDB will bind to the "DN"(distinguished name) constructed as `{prefix}{username}{suffix}`. Typically, the `prefix` parameter is used to specify `cn=`, and the `suffix` is used to specify the remaining part of the DN. The `username`, of course, is provided by the client. Here's the configuration file example for the "simple bind" mode in GreptimeDB's LDAP user provider: ```toml # Name or IP address of the LDAP server to connect to. server = "127.0.0.1" # Port number on LDAP server to connect to. port = 636 # Set to "ldap" to use LDAP, "ldaps" to use LDAPS. # The connection between GreptimeDB and the LDAP server starts as an initially unencrypted one, # then upgrades to TLS as the first action against the server, per the LDAPv3 standard ("StartTLS"). scheme = "ldaps" # The authentication mode to the LDAP server, either `bind = "simple"` or `bind = "search"`. [auth_mode] # The following options are used in simple bind mode only: bind = "simple" # String to prepend to the username when forming the DN to bind as, when doing simple bind authentication. prefix = "cn=" # String to append to the username when forming the DN to bind as, when doing simple bind authentication. suffix = ",dc=example,dc=com" ``` In the "search bind" mode, GreptimeDB will first try to bind to the LDAP directory with a fixed username and password, which are set in the configuration file (`bind_dn` and `bind_passwd`), Then GreptimeDB performs a search for the user trying to log in to the database. The search will be performed over the subtree at `base_dn`, filtered by the `search_filter`, and will try to do an exact match of the attribute specified in `search_attribute`. Once the user has been found in this search, GreptimeDB re-binds to the directory as this user, using the password specified by the client, to verify that the login is correct. This method allows for significantly more flexibility in where the user objects are located in the directory, but will cause two additional requests to the LDAP server to be made. The following toml snippets show the configuration file example for the "search bind" mode in GreptimeDB's LDAP user provider. The common parts of `server`, `port`, and `scheme` as shown in the "simple bind" mode configuration file above are omitted: ```toml [auth_mode] # The following options are used in search bind mode only: bind = "search" # Root DN to begin the search for the user in, when doing search bind authentication. base_dn = "ou=people,dc=example,dc=com" # DN of user to bind to the directory with to perform the search when doing search bind authentication. bind_dn = "cn=admin,dc=example,dc=com" # Password for user to bind to the directory with to perform the search when doing search bind authentication. bind_passwd = "secret" # Attribute to match against the username in the search when doing search bind authentication. # If no attribute is specified, the uid attribute will be used. search_attribute = "cn" # The search filter to use when doing search bind authentication. # Occurrences of "$username" will be replaced with the username. # This allows for more flexible search filters than search_attribute. search_filter = "(cn=$username)" ``` ## Use LDAP user provider in GreptimeDB To use the LDAP user provider, first config your LDAP authentication mode like above, then start GreptimeDB with the `--user-provider` parameter set to `ldap_user_provider:`. For example, if you have a configuration file `/home/greptimedb/ldap.toml`, you can start a GreptimeDB standalone server with the following command: ```shell greptime standalone start --user-provider=ldap_user_provider:/home/greptimedb/ldap.toml ``` Now you can create a connection to GreptimeDB using your LDAP user accounts. :::tip NOTE If you are using the MySQL CLI to connect to GreptimeDB that is configured with LDAP user provider, you need to specify the `--enable-cleartext-plugin` in the MySQL CLI. ::: --- ## Backup ## Backup Metadata The metadata store of a GreptimeDB cluster can be stored in etcd or a relational database. For production clusters, it is recommended to use cloud provider managed relational database services (RDS) with periodic backup features enabled. Please refer to [Metadata Export & Import documentation](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-meta-data.md) for the details. ## Backup Database Since object storage typically maintains multiple replicas, if GreptimeDB data is stored on object storage, additional database backups are usually unnecessary. You may also consider enabling versioning features of object storage to prevent data loss from accidental operations. You can also use GreptimeDB's `COPY DATABASE` functionality to create backups. For more information, please refer to the [Data Export & Import documentation](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-data.md). --- ## Deploy a GreptimeDB Cluster with Datanode Groups In this guide, you will learn how to deploy a GreptimeDB cluster on Kubernetes with a datanode groups consisting of multiple datanode instances. :::tip NOTE This feature is for the Enterprise Edition only. Please do not use open-source image to enable this feature. Please [contact us](https://greptime.com/contactus) if you want to use it. ::: ## Prerequisites - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.18.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [GreptimeDB Operator](https://github.com/GrepTimeTeam/greptimedb-operator) >= v0.3.0 ## Upgrade operator Install the GreptimeDB Operator, setting the image version to be greater than or equal to `v0.3.0`. For detailed instructions on upgrading the operator, please refer to the [GreptimeDB Operator Management](/user-guide/deployments-administration/deploy-on-kubernetes/greptimedb-operator-management.md#upgrade) guide. ## Datanode Groups Configuration In the enterprise edition, you can configure **datanode groups** to separate read and write workloads into different groups. The datanode accepts a `workload_types` field to distinguish its workload type. Supported types are **`hybrid`**, **`query`**, and **`ingest`**: * **`hybrid`** is the default and acts as a superset of `query` and `ingest`, allowing the datanode to handle both workloads. * **`query`** is optimized for read workloads, datanode only handle read workload. * **`ingest`** is optimized for write workloads, datanode only handle write workload. While `hybrid` is convenient, running both reads and writes on the same datanode may cause them to interfere with each other, for example, a large query may occupy too many resources, thus affecting the online ingestion. For best performance, it is recommended to separate read and write workloads into different datanode groups. When configuring datanode groups, ensure that each group includes a `name` field. The following `values.yaml` example demonstrates how to define separate datanode groups: ```yaml danodata: enabled: false datanodeGroups: - name: write replicas: 1 config: | workload_types = ["ingest"] template: main: resources: requests: cpu: '4' memory: 8Gi storage: fs: storageClassName: ${storageClassName} storageSize: 100Gi - name: read replicas: 1 config: | workload_types = ["query"] template: main: resources: limits: cpu: '8' memory: 16Gi meta: replicas: 1 backendStorage: etcd: endpoints: - "etcd.etcd-cluster.svc.cluster.local:2379" frontend: replicas: 1 ``` For guidance on configuring alternative metadata storage backends for Metasrv, refer to the [Metadata Storage Configuration](/user-guide/deployments-administration/manage-metadata/configuration.md) documentation. You can use the following command to apply the configuration: ``` helm upgrade --install ${release-name} greptime/greptimedb-cluster --namespace ${namespace} -f values.yaml ``` ## Verify the Installation Check the status of the pods: ```bash kubectl get pods -n ${namespace} NAME READY STATUS RESTARTS AGE ${release-name}-datanode-read-0 1/1 Running 0 30s ${release-name}-datanode-write-0 1/1 Running 0 30s ${release-name}-flownode-0 1/1 Running 0 60s ${release-name}-frontend-774c76cffc-znvrw 1/1 Running 0 30s ${release-name}-meta-58977b7897-8k2sf 1/1 Running 0 90s ``` ## Next steps - For best performance, it is recommended to [Configure frontend groups](/user-guide/deployments-administration/deploy-on-kubernetes/configure-frontend-groups.md), which ensures complete separation of read and write traffic, achieving maximum isolation. - Add Read Replica for your table, please refer to [Read Replica](/enterprise/read-replicas/overview.md). --- ## GreptimeDB Enterprise Deployment Guide ## Environment Requirements - [Docker](https://docs.docker.com/get-started/get-docker/) >= v23.0.0 - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) >= v1.21.0 - [Helm](https://helm.sh/docs/intro/install/) >= v3.0.0 - [kubernetes](https://kubernetes.io/) >= 1.21 ## Overview The GreptimeDB Enterprise Edition deployment includes the following components: 1. GreptimeDB Operator: Used to interact with the Kubernetes cluster. 2. KV Store: Used to store database metadata (supports cloud services or self-hosted). It is recommended to use RDS from cloud services such as AWS RDS PostgreSQL. Optional storage facilities include: - PostgreSQL - MySQL - ETCD 3. GreptimeDB Database Cluster, including the following components: - Meta:Database cluster metadata management component - Datanode:Data node - Frontend:Entry point and protocol parsing node - Flownode(optional): Flow computation node for continuous aggregation workloads - Vector Sidecar:Metrics collection agent - GreptimeDB Standalone: Cluster self-monitoring storage node 4. GreptimeDB Enterprise Dashboard 5. Kafka(optional): Provides Remote WAL for GreptimeDB 6. MinIO(optional): Provides object storage for GreptimeDB. It is recommended to use object storage from cloud services (e.g., AWS S3) Components marked with * are optional: | Pod Component Name | Replicas | CPU (Core) | Memory (GB) | Disk (Gi) | |----------------------------------|----------|------------|-------------|-----------| | ETCD`*` | 3 | 2 | 4 | 10 | | GreptimeDB Operator | 1 | 1 | 1 | | | Meta | | | | | | Datanode | | | | | | Frontend | | | | | | Flownode`*` | | | | | | Vector Sidecar | | | | | | GreptimeDB Standalone | 1 | 4 | 8 | | | GreptimeDB Enterprise Dashboard | 1 | | | | | Kafka`*` | 3 | | | | | MinIO`*` | 4 | | | | ## Deploy GreptimeDB Operator Refer to [GreptimeDB Operator Management Documentation](/user-guide/deployments-administration/deploy-on-kubernetes/greptimedb-operator-management.md) for detailed installation steps. ## Deploy ETCD (Option) Refer to [Manage ETCD](/user-guide/deployments-administration/manage-metadata/manage-etcd.md) for detailed installation steps. ## Deploy Kafka (Option) Refer to [Deploy Kafka Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-kafka.md) for detailed installation steps. ## Deploy MinIO (Option) Refer to [Deploy MinIO Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-minio.md) for detailed installation steps. ## Install GreptimeDB Infrastructure Test Refer to [Deploy GreptimeDB Infrastructure test](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-infra-test.md) for detailed installation steps. Tell the test results to Greptimeteam. ## Install and Start GreptimeDB ### Obtain GreptimeDB Enterprise Edition Image GreptimeDB Enterprise Edition is distributed as a Docker image. We provide each customer with a dedicated private Docker repository hosted on Alibaba Cloud. You can pull it directly using the docker pull command or configure it in the Helm chart. You need to configure the image information in the Helm chart's `values.yaml` file to obtain your dedicated GreptimeDB Enterprise Edition, for example: ```yaml customImageRegistry: enabled: true # -- pull secret name, customizable, must match `image.pullSecrets` secretName: greptimedb-custom-image-pull-secret registry: username: password: image: registry: repository: tag: pullSecrets: - greptimedb-custom-image-pull-secret ``` In the above configuration: - `registry`, `username`, and `password` under `customImageRegistry` are used to create the Kubernetes pull secret - `registry`, `repository`, and `tag` under `image` are used to specify the GreptimeDB Enterprise Edition image - Therefore, `customImageRegistry.secretName` and `image.pullSecrets` must match to ensure correct authentication when pulling the image Please contact Greptime staff to obtain the specific values for the above configuration items. When Greptime staff first deliver the GreptimeDB Enterprise Edition to you, they will inform you of the image registry address, username, and password via email or other means. Please keep this information safe and do not share it with external parties! ### Configuration Management The following are tools and documentation that can help with configuration: - tool: https://enterprise-wizard.greptime.com/ - documentation: [common-helm-chart-configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) Before installation, you need to create a file to configure the GreptimeDB cluster. Adjust it according to your Kubernetes environment. Below is a reference configuration for `greptimedb-cluster-values.yaml`: ```yaml customImageRegistry: enabled: true # -- pull secret name, customizable, must match `image.pullSecrets` secretName: greptimedb-custom-image-pull-secret registry: username: password: image: registry: repository: tag: pullSecrets: - greptimedb-custom-image-pull-secret additionalLabels: {} initializer: registry: docker.io repository: greptime/greptimedb-initializer tag: "v0.6.1" # -- Meta configuration meta: # -- Meta replicas replicas: 2 backendStorage: # Optional # KV storage configuration, this configuration connects to ETCD etcd: endpoints: ["etcd.etcd-cluster.svc.cluster.local:2379"] # Below is an example using PostgreSQL as the KV store: # postgresql: # host: "postgres.default.svc.cluster.local" # port: 5432 # database: "metasrv" # table: "greptime_metakv" # electionLockID: 1 # credentials: # secretName: "meta-postgresql-credentials" # username: "root" # password: "root" # Below is an example using MySQL as the KV store: # mysql: # host: "mysql.default.svc.cluster.local" # port: 3306 # database: "metasrv" # table: "greptime_metakv" # electionLockID: 1 # credentials: # secretName: "meta-mysql-credentials" # username: "root" # password: "root" rbac: create: true podTemplate: serviceAccount: create: true main: # Meta resource configuration resources: requests: cpu: '2' memory: 4Gi limits: cpu: '2' memory: 4Gi affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchLabels: app.greptime.io/component: greptimedb-meta topologyKey: kubernetes.io/hostname weight: 1 # -- Datanode configuration datanode: # -- Datanode replicas replicas: 3 # -- Datanode configuration configData: |- [[region_engine]] [region_engine.mito] write_cache_size = "20G" write_cache_ttl = "7d" podTemplate: # serviceAccount: # # Specifies whether a service account should be created # create: false # # Annotations to add to the service account # annotations: {} main: # -- Datanode resource resources: requests: cpu: '8' memory: 16Gi limits: cpu: '8' memory: 16Gi affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchLabels: app.greptime.io/component: greptimedb-datanode topologyKey: kubernetes.io/hostname weight: 1 # -- Datanode local disk configuration storage: storageClassName: null # -- Local disk size storageSize: 100Gi # -- Storage retain policy for datanode persistent volume storageRetainPolicy: Retain # -- Frontend configuration frontend: # -- Frontend replicas replicas: 3 podTemplate: main: # Frontend resource resources: requests: cpu: '8' memory: 16Gi limits: cpu: '8' memory: 16Gi affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchLabels: app.greptime.io/component: greptimedb-frontend topologyKey: kubernetes.io/hostname weight: 1 # -- Flownode configuration flownode: # -- Whether to deploy flownode enabled: false # -- Flownode replicas replicas: 1 podTemplate: main: resources: requests: cpu: '8' memory: 16Gi limits: cpu: '8' memory: 16Gi # -- Database self-monitoring configuration monitoring: # -- Enable self-monitoring enabled: true # -- The default retention time for monitoring data ttl: 30d standalone: base: # serviceAccountName: "greptimedb-datanode" imagePullSecrets: - name: "greptimedb-custom-image-pull-secret" main: # Self-monitoring resource configuration resources: requests: cpu: '4' memory: 8Gi limits: cpu: '4' memory: 8Gi # Self-monitoring standalone storage location, can be optionally configured for object storage # objectStorage: # cache: # cacheCapacity: "50GiB" # s3: # secretName: "storage-credentials" # bucket: "greptimedb-bucket" # region: "ap-southeast-1" # root: "greptimedb-monitor-data" # endpoint: "http://minio.minio:9000" # Self-monitoring local disk size datanodeStorage: fs: storageClassName: null storageSize: 100Gi # sidecar vector configuration vector: registry: docker.io repository: timberio/vector tag: "0.49.0-debian" # sidecar vector resource configuration resources: requests: cpu: '1' memory: "1Gi" limits: cpu: '1' memory: "1Gi" # Object storage related configuration, enable as needed # Using MinIO # objectStorage: # cache: # cacheCapacity: "50GiB" # s3: # bucket: "greptimedb-bucket" # region: "ap-southeast-1" # root: "greptimedb-data" # endpoint: "http://minio.minio:9000" # Enable Enterprise Edition user and permission configuration auth: enabled: true useBuiltIn: true mountPath: "/etc/greptimedb/auth" fileName: "passwd" users: # Default admin username, modify as needed - username: "superuser" # Initial admin account password, modify as needed password: "1fa44bbc-5ded-42bd-a3f1-c3621affce63" permission: "admin" # Audit logging configuration(Enterprise GreptimeDB only) # Audit logs record operations executed on the database, helping monitor user activity, # detect suspicious operations, and ensure compliance. # See https://docs.greptime.com/enterprise/deployments-administration/monitoring/audit-logging/ auditLog: # Enable the audit log plugin. Default: false. enabled: true # Allowed sources for auditing. This option acts as a filter. # Multiple sources are separated by commas (","). # Available sources: "unknown", "http", "grpc", "mysql", "postgres". # A special value "all" (default) means all sources. sources: "all" # Allowed statement classes for auditing. This option acts as a filter. # Multiple classes are separated by commas (","). # Available classes: "read", "write", "admin", "ddl", "misc". # A special value "all" means all classes. Default: "ddl,admin". classes: "ddl,admin" # Allowed statement commands for auditing. This option acts as a filter. # Multiple commands are separated by commas (","). # Available commands: "promql", "select", "copy", "insert", "delete", "create", # "alter", "truncate", "drop", "admin", "misc". # A special value "all" (default) means all commands. commands: "all" # Allowed object types for auditing. This option acts as a filter. # Multiple object types are separated by commas (","). # Available object types: "database", "table", "view", "flow", "index", "misc", "trigger". # A special value "all" (default) means all object types. objectTypes: "all" # Maximum number of audit log files to retain. Default: 30. maxLogFiles: 30 # Remote WAL related configuration, enable as needed # remoteWal: # enabled: true # kafka: # brokerEndpoints: # - "kafka-broker-0.kafka-broker-headless.kafka.svc.cluster.local:9092" # - "kafka-broker-1.kafka-broker-headless.kafka.svc.cluster.local:9092" # - "kafka-broker-2.kafka-broker-headless.kafka.svc.cluster.local:9092" ``` ### Start GreptimeDB Install the GreptimeDB cluster in the `greptimedb` namespace: ```bash helm upgrade --install greptimedb \ --create-namespace \ greptime/greptimedb-cluster \ -n greptimedb --values greptimedb-cluster-values.yaml ``` Verify the GreptimeDB installation: ```bash kubectl get pod -n greptimedb ```
Expected output ```bash NAME READY STATUS RESTARTS AGE greptimedb-datanode-0 2/2 Running 0 2m33s greptimedb-datanode-1 2/2 Running 0 2m33s greptimedb-datanode-2 2/2 Running 0 2m33s greptimedb-frontend-74999c79cc-pzj8w 2/2 Running 0 17s greptimedb-frontend-74999c79cc-rm2fb 2/2 Running 0 17s greptimedb-frontend-74999c79cc-zbtdg 2/2 Running 0 17s greptimedb-meta-56dc894867-jpt5c 2/2 Running 0 4m29s greptimedb-meta-56dc894867-tpw4c 2/2 Running 0 4m29s greptimedb-meta-56dc894867-wmh1t 2/2 Running 0 4m29s greptimedb-monitor-standalone-0 1/1 Running 0 4m42s ```
## Deploy Enterprise Dashboard ### Configuration Management Before installation, you need to create a file `dashboard-values.yaml` to configure the dashboard. Below is a configuration example: ```yaml replicaCount: 1 image: # Please contact Greptime staff for the value repository: tag: pullPolicy: IfNotPresent imagePullSecrets: - name: greptimedb-custom-image-pull-secret nameOverride: "" fullnameOverride: "" config: | servicePort: 19095 logLevel: info enableLicenseManager: true enableUserAuthentication: true backendStore: type: sqlite sqlite: dataDir: /data provisionedInstances: - name: greptimedb namespace: greptimedb type: cluster settings: basic: url: http://greptimedb-frontend.greptimedb.svc.cluster.local:4000 meta_url: http://greptimedb-meta.greptimedb.svc.cluster.local:4000 monitoring: greptimedb: url: http://greptimedb-monitor-standalone.greptimedb.svc.cluster.local:4000 license: secret_name: greptimedb-license secret_namespace: greptimedb servicePort: 19095 serviceAccount: create: true annotations: {} name: "" podAnnotations: {} podSecurityContext: {} # fsGroup: 2000 securityContext: {} service: type: ClusterIP port: 19095 annotations: {} resources: requests: cpu: '1' memory: 1Gi limits: cpu: '1' memory: 1Gi nodeSelector: {} tolerations: [] affinity: {} ``` ### Install ```bash helm upgrade --install greptimedb-enterprise-dashboard \ oci://greptime-registry.cn-hangzhou.cr.aliyuncs.com/charts/greptimedb-enterprise-dashboard \ -n greptimedb \ --values dashboard-values.yaml ``` Verify the Enterprise Dashboard installation: ```bash kubectl get pod -n greptimedb | grep enterprise-dashboard ```
Expected output ```bash greptimedb-enterprise-dashboard-67f498d6f9-n89z5 1/1 Running 0 27s ```
```bash kubectl get svc -n greptimedb | grep enterprise-dashboard ```
Expected output ```bash greptimedb-enterprise-dashboard ClusterIP 10.96.80.175 19095/TCP 89s ```
### Login Access port 19095 of the dashboard service to log in. ![Enterprise Dashboard Login](/enterprise-dashboard-login.png) Log in using the `auth.users[0].username` and `auth.users[0].password` from the database deployment configuration. For more console operation reference [documentation](/enterprise/console-ui.md). --- ## Deploy GreptimeDB on Kubernetes(Deploy-on-kubernetes) The deployment process for GreptimeDB Enterprise on Kubernetes is almost the same as the open-source version. This chapter focuses on the specific deployment configurations and features of the enterprise version. ## Installation To learn how to start GreptimeDB through Helm Chart, please see the [Install GreptimeDB](./installation.md) page. ## Upgrade Please visit the [Upgrade GreptimeDB](./upgrade.md) page to learn how to upgrade GreptimeDB enterprise. --- ## Upgrade(Deploy-on-kubernetes) ## Upgrade Cluster with GreptimeDB Operator Upgrading the GreptimeDB Enterprise Edition image is straightforward. Simply modify the `tag` in the Helm chart and restart. ## Upgrading Cluster Without GreptimeDB Operator When upgrading a cluster without the GreptimeDB Operator, you must manually enable Metasrv maintenance mode before operating on each component (e.g., rolling upgrade of Datanode nodes). After the upgrade is complete, wait for all components to return to a healthy state before disabling Metasrv maintenance mode. When Metasrv maintenance mode is enabled, the Auto Balancing (if enabled) and Region Failover (if enabled) mechanisms in the cluster will be suspended until maintenance mode is disabled. Please refer to [Managing Maintenance Mode](/user-guide/deployments-administration/maintenance/maintenance-mode.md#managing-maintenance-mode) for detailed instructions on how to enable and disable Metasrv maintenance mode. --- ## DR Solution Based on Active-Active Failover GreptimeDB Enterprise can deploy two standalone nodes as peers. Both nodes accept reads and writes, keep a complete copy of the data, and asynchronously replicate data changes to each other. Neither node is a permanently designated primary. This topology is intended for edge and small-to-medium deployments that need node-level or site-level disaster recovery without operating a distributed GreptimeDB cluster. A load balancer, client driver, or service-discovery system directs traffic to an available node. ## Architecture ![Two GreptimeDB nodes replicate data in both directions, with traffic managed by an external failover mechanism](/img/active-active-forwarding.svg) The two nodes operate independently: - A write is committed on the receiving node and then replicated to the peer. The client does not wait for the peer to acknowledge the write. - A query runs on the node that receives it. GreptimeDB does not merge query results from both nodes. - Continuous network connectivity between the nodes is not required. When a peer or the network is unavailable, pending changes are retained locally and sent after connectivity is restored, provided that the source node's storage remains available and has sufficient capacity. - Replicated changes are not sent back to their source, preventing circular replication. Because replication is asynchronous, the peer can temporarily return stale data. Applications that require read-after-write consistency should keep related reads on the node that accepted the write, or wait for replication to catch up before switching nodes. ## Capabilities and Boundaries | Capability | Behavior | | --- | --- | | Node and site redundancy | Each node keeps a complete data copy and can serve application traffic. | | Write replication | Data changes are replicated asynchronously in both directions. | | Query execution | Queries execute locally on the node receiving the request. | | Network interruption | A healthy node continues serving, and pending changes are replicated after the peer recovers. | | Traffic failover | An external load balancer, client driver, or service-discovery system switches endpoints. | | Consistency | Data on the two nodes is eventually consistent. | | Scale-out | This topology provides two-node redundancy; it is not a replacement for a distributed cluster. | The following limitations affect DR planning: - A successful write does not guarantee that the peer has already received it. - If a node and its local storage are permanently lost before pending changes reach the peer, those changes may be unavailable on the peer. - Schema changes made while the peer is unavailable may require verification or manual reconciliation after recovery. - The topology does not elect a traffic primary or automatically switch client connections. If the deployment requires synchronous replication, strict zero RPO after the complete loss of one site's local storage, or horizontal scale-out, use a distributed DR architecture based on Remote WAL and cross-region object storage instead. ## Plan the Deployment Before enabling the topology: 1. Deploy the nodes in separate failure domains. For site-level DR, use different availability zones or regions. 2. Ensure that each node has enough storage for its complete data copy and for changes accumulated during the longest expected peer outage. 3. Choose an external traffic failover mechanism and define its health checks, retry policy, and connection-draining behavior. 4. Assign non-overlapping table ID ranges so that both nodes can create tables independently without ID conflicts. 5. Define how the application handles ambiguous write results and retries. Prefer idempotent writes where possible. ## Configuration Configure each node with the other node as a synchronization target. The following example shows the relevant settings on node A: ```toml [[sync_nodes]] name = "node-b" enabled = true [sync_nodes.metasrv] metasrv_addrs = ["10.0.2.10:3002"] [sync_nodes.region_server] id = 2 addr = "10.0.2.10:4001" [table_id_range] start = 1024 end = 1000000 ``` Configure node B with node A's endpoints and a different `table_id_range`. Each `sync_nodes.name` must be unique and may contain only ASCII letters, digits, `_`, or `-`. The exact endpoints and table ID ranges depend on the deployment. Contact Greptime for a reviewed production configuration. ## Failure and Recovery Behavior | Event | Expected behavior | Operator action | | --- | --- | --- | | Peer or inter-site network is unavailable | The healthy node continues serving. Pending changes wait for the peer to recover. | Keep traffic on the healthy node and monitor replication health and local storage capacity. | | One node is unavailable | Traffic sent to that node fails until the external failover mechanism redirects it. | Confirm the surviving node is healthy, then switch or drain traffic. | | The unavailable node returns | Pending changes are replicated automatically. The nodes may differ until catch-up completes. | Keep traffic stable and verify data and schema before restoring normal routing. | | The source node's local storage is full or unavailable | New writes may be rejected to preserve recoverability. | Restore local storage capacity and retry failed writes according to application semantics. | | A node and its storage are permanently lost | Changes not yet replicated may be missing from the surviving node. | Recover from another data copy if available and assess the actual RPO. | ### Fail over traffic When a node or site fails: 1. Confirm that the alternate node is reachable and can serve critical queries. 2. Stop sending new traffic to the failed endpoint. 3. Direct traffic to the healthy node through the selected failover mechanism. 4. Verify application error rates, write success, and critical query results. 5. Keep routing stable until the failed node has recovered and caught up. Avoid repeatedly switching write traffic while the nodes cannot communicate. Although both nodes accept writes, independent writes on disconnected nodes increase the amount of state that must converge and complicate recovery verification. ### Recover a node After the failed node or network returns: 1. Keep application writes on the healthy node. 2. Restore the recovered node and its synchronization connectivity. 3. Wait until replication is healthy and the backlog has been processed. 4. Compare critical table schemas and recent data on both nodes. 5. Run application-level read and write probes against the recovered node. 6. Gradually restore traffic and continue monitoring both nodes. Treat schema changes made during the outage as a separate verification item. Do not restore production traffic to the recovered node until critical schemas match. ## RPO and RTO ### RPO RPO depends on the failure mode and the amount of data waiting to be replicated: - If only the peer or network fails and the source node's storage remains intact, pending changes can be sent after recovery. - If the source node and its storage are lost before replication completes, the surviving node may not contain the pending changes. The topology does not provide a synchronous replication mode. Measure replication health under representative load and include the observed delay in the DR plan. ### RTO RTO includes failure detection, endpoint switching, connection retry, and application recovery time. GreptimeDB does not perform traffic failover inside this two-node topology, so the external traffic layer and its configuration determine most of the RTO. Test both planned and unplanned failover regularly. The test should cover active connections, connection pools, in-flight writes, and the time required to validate the alternate node. ## Choose a Traffic Failover Mechanism - **Load balancer.** Configure active health checks and remove an unhealthy endpoint from rotation. A managed load balancer or a separate HAProxy instance keeps the failover policy outside applications. - **Client driver.** Some MySQL and PostgreSQL drivers accept multiple hosts and retry another endpoint. For example, see [MySQL Connector/J failover](https://dev.mysql.com/doc/connector-j/en/connector-j-config-failover.html) and [PostgreSQL JDBC connection failover](https://jdbc.postgresql.org/documentation/use/#connection-fail-over). Connection failover does not make retrying a non-idempotent write safe. - **Service discovery or endpoint update.** Health automation can update DNS, a service registry, or the application's endpoint set. Include health-check intervals, DNS TTL, and connection-pool refresh time in the RTO estimate. ### HAProxy example The following minimal configuration exposes the GreptimeDB HTTP API through a local TCP endpoint at `127.0.0.1:14000`. HAProxy sends traffic to node A while it is healthy and switches to node B after three consecutive failed health checks. Node A becomes eligible again after two consecutive successful checks. Although both GreptimeDB nodes can accept writes, this example uses active-standby traffic routing to keep application writes on one node during normal operation. Replace the hostnames, ports, bind address, and timeouts for your environment. ```text title="haproxy.cfg" global log /dev/log local0 log /dev/log local1 notice daemon defaults mode tcp log global option tcplog timeout connect 5s timeout client 30s timeout server 30s frontend greptimedb_http bind 127.0.0.1:14000 default_backend greptimedb_http_nodes backend greptimedb_http_nodes # Preferred node server node-a node-a:4000 check inter 2s fall 3 rise 2 # Failover node server node-b node-b:4000 check inter 2s fall 3 rise 2 backup ``` This configuration proxies only the HTTP API on port `4000`. To proxy the MySQL or PostgreSQL protocol, create separate frontend and backend pairs that target port `4002` or `4003`, respectively. Validate the configuration before starting HAProxy: ```shell haproxy -c -f haproxy.cfg haproxy -f haproxy.cfg ``` ## Operational Checklist Monitor both nodes for availability, replication errors, replication delay, and storage capacity. Also use an end-to-end probe that writes to one node and verifies the result on the peer. Before using the topology in production, verify that: - both nodes accept reads and writes; - changes in either direction reach the peer; - table creation on either node does not cause ID conflicts; - traffic switches within the target RTO; - pending changes catch up after an extended network interruption; - critical schemas and recent data match before a recovered node receives traffic; - the application handles connection loss and ambiguous write results safely. :::tip NOTE To compare RPO, RTO, and infrastructure requirements across disaster recovery options, see [Solution Comparison](/user-guide/deployments-administration/disaster-recovery/overview.md#solution-comparison). ::: --- ## Disaster Recovery(Disaster-recovery) As a distributed database, GreptimeDB provides different disaster recovery (DR) solutions. Please refer to the [Disaster Recovery Overview](/user-guide/deployments-administration/disaster-recovery/overview.md) in the GreptimeDB OSS documentation to learn about all the disaster recovery solutions provided by Greptime. This section only introduces the solutions provided in GreptimeDB Enterprise. - [DR Solution Based on Active-Active Failover](./dr-solution-based-on-active-active-failover.md) --- ## Configure Environment Affinity for Region Placement The env affinity selector is an Enterprise-only Metasrv plugin that places newly created table regions on datanodes in the same environment as the frontend that receives the DDL request. Its main purpose is to prevent unexpected cross-AZ or cross-region traffic, which can introduce extra latency and unexpected network transfer costs. It is useful when frontends and datanodes are deployed across availability zones, regions, or other failure domains, and you want table creation to keep region placement close to the request origin. :::tip NOTE This feature is for the Enterprise Edition only. Please do not use the open-source image to enable this feature. Please [contact us](https://greptime.com/contactus) if you want to use it. ::: ## How it works When `env_affinity_selector` is enabled, Metasrv wraps the configured datanode selector: 1. Metasrv reads the origin frontend from the DDL request selector context. 2. Metasrv reads the configured affinity environment variables from that frontend's heartbeat information. 3. Metasrv filters candidate datanodes to those with matching environment variable values. 4. Metasrv delegates the final selection among the matching datanodes to the base selector configured by the top-level `selector` option. For example, if a `CREATE TABLE` request reaches a frontend whose heartbeat reports `REGION=us-east` and `AZ=az-a`, Metasrv first filters candidate datanodes to those reporting the same `REGION` and `AZ`, then applies the configured base selector, such as `load_based`, to choose the final datanodes. Requests that do not carry the origin frontend selector context bypass the env affinity selector and use the base selector directly. This keeps non-DDL selector paths unchanged. ## Configure heartbeat environment variables The frontend and datanode processes must report the same affinity keys in their heartbeat information. Configure `heartbeat_env_vars` as a top-level option for both frontends and datanodes: ```toml heartbeat_env_vars = ["REGION", "AZ"] ``` Each process must also have the corresponding environment variables set at startup: ```shell REGION=us-east AZ=az-a ``` :::warning Do not include sensitive variables in `heartbeat_env_vars`. The selected values are sent to Metasrv through heartbeat messages and stored in Metasrv node information. ::: For general heartbeat configuration, see [Heartbeat configuration](/user-guide/deployments-administration/configuration.md#heartbeat-configuration). ## Configure Metasrv Enable `env_affinity_selector` in the Enterprise plugin configuration for Metasrv: ```toml # The base datanode selector. Env affinity filters candidates first, then uses # this selector to choose from the matching datanodes. selector = "load_based" [[plugins]] [plugins.env_affinity_selector] enable = true # Environment variable names that must match between the origin frontend and # candidate datanodes. The default value is ["AZ"]. affinity_keys = ["REGION", "AZ"] # Optional. The default value is "origin_frontend_addr" and usually does not # need to be changed. # origin_frontend_addr_extension_key = "origin_frontend_addr" # Controls behavior when env affinity cannot be applied or when there are not # enough same-env datanode candidates. Valid values are "error" and # "base_selector". The default value is "error". fallback_selector = "base_selector" ``` The top-level `selector` option still selects the base datanode selector. Supported base selectors include `round_robin`, `lease_based`, and `load_based`. Env affinity does not replace the base selector; it constrains the candidate datanodes before the base selector makes the final choice. ## Fallback behavior Use `fallback_selector` to control what Metasrv does when env affinity cannot be applied. | Value | Behavior | | --- | --- | | `error` | Return an error when the affinity context is invalid or there are not enough same-env datanode candidates. This is the default and prevents placement outside the matching environment. | | `base_selector` | Log a warning and fall back to the base selector configured by the top-level `selector` option. The fallback can select any datanode accepted by the base selector. | Invalid affinity context includes: - The origin frontend is not active. - The frontend heartbeat is missing one or more configured `affinity_keys`. - `affinity_keys` is empty. Insufficient same-env candidates includes: - No datanode matches the origin frontend environment. - The selector requires multiple datanodes, duplicate selection is not allowed, and the number of matching datanodes is smaller than the required number. The legacy `strict` option is not supported. Configurations that still specify `strict` are rejected. ## Kubernetes example The following Helm values show a cluster deployed across two availability zones. Frontends and datanodes report `REGION` and `AZ` through heartbeat extensions, and Metasrv enables env affinity placement. ```yaml frontend: replicas: 2 configData: | heartbeat_env_vars = ["REGION", "AZ"] podTemplate: main: env: - name: REGION value: us-east - name: AZ valueFrom: fieldRef: fieldPath: metadata.labels['topology.kubernetes.io/zone'] datanodeGroups: - name: az-a replicas: 3 config: | heartbeat_env_vars = ["REGION", "AZ"] template: main: env: - name: REGION value: us-east - name: AZ value: az-a - name: az-b replicas: 3 config: | heartbeat_env_vars = ["REGION", "AZ"] template: main: env: - name: REGION value: us-east - name: AZ value: az-b meta: configData: | selector = "load_based" [[plugins]] [plugins.env_affinity_selector] enable = true affinity_keys = ["REGION", "AZ"] fallback_selector = "error" ``` After applying the configuration, create a table through a frontend in each environment and use `SHOW REGION` to verify where the new regions are placed. For more information, see [SHOW statement extensions](/reference/sql/show.md#extensions-to-show-statements). ## Best practices - Use stable, low-cardinality keys such as `REGION`, `AZ`, or `ZONE`. - Configure the same `heartbeat_env_vars` on every frontend and datanode that participates in DDL placement. - Start with `fallback_selector = "error"` when strict placement is required. - Use `fallback_selector = "base_selector"` only when availability is more important than staying within the matching environment. - Use `load_based` as the base selector when you want env-affinity placement and load-aware distribution within the matching environment. --- ## Audit Logging Database audit logging is the process of recording the actions performed on the database. The audit logs help monitor user activities, detect suspicious actions, and ensure compliance with regulations inside or outside of an organization. This document provides an overview of audit logging in GreptimeDB and how to configure it. ## Overview A statement (SQL or PromQL) that is executed on GreptimeDB is recorded in the audit log (if it is configured to be audited, of course). This is an example record in the audit log: ```json { "time": "2024-11-05T06:13:19.903713Z", "user": "greptime_user", "source": "Mysql", "class": "Ddl", "command": "Create", "object_type": "Database", "object_names": [ "audit_test" ], "statement": "CREATE DATABASE audit_test" } ``` As you can see, the record is formatted as a JSON string. It contains the following fields: - `time`: The time when the statement was executed. It's formatted as an ISO 8601 date and time string with UTC timezone. - `user`: The user who sends the request. - `source`: The protocol used to connect to GreptimeDB. - `class`: The class of the statement, like "Read", "Write" or "DDL" etc. - `command`: The command of the statement, like "Select", "Insert" or "Create" etc. - `object_type`: The type of the object that the statement operates on, like "Database", "Table" or "Flow" etc. - `object_names`: The names of the objects that the statement operates on. - `statement`: The statement itself. ## Configuration Audit logging is provided as a plugin in GreptimeDB. To enable and configure it, add the following TOML to your GreptimeDB config file: ```toml [[plugins]] # Add the audit log plugin to your GreptimeDB. [plugins.audit_log] # Whether to enable audit logging, defaults to true. enable = true # The directory to store the audit log files. Defaults to "/tmp/greptimedb/logs". dir = "/tmp/greptimedb/logs" # The allowed auditing sources. This option works as a filter: # if a statement is not coming from one of these configured sources, it won't be recorded in the audit logs. # Multiple sources are separated by comma(","). # All sources are: "unknown", "http", "grpc", "mysql" and "postgres". # A special "all" (which is the default value) means all the sources. sources = "all" # The allowed auditing classes. This option works as a filter: # if a statement's class do not match one of these configured values, it won't be recorded in the audit logs. # Multiple classes are separated by comma(","). # All classes are: "read", "write", "admin", "ddl" and "misc". # A special "all" means all the classes. # Defaults to "ddl" and "admin". classes = "ddl,admin" # The allowed auditing commands. This option works as a filter: # if a statement's command do not match one of these configured values, they won't be recorded in the audit logs. # Multiple commands are separated by comma(","). # All commands are: "promql", "select", "copy", "insert", "delete", "create", "alter", "truncate", "drop", "admin" and "misc". # A special "all" (which is the default value) means all the commands. commands = "all" # The allowed auditing object types. This option works as a filter: # if a statement's target object do not match one of these configured values, they won't be recorded in the audit logs. # Multiple object types are separated by comma(","). # All object types are: "database", "table", "view", "flow", "index", "misc", and "trigger". # A special "all" (which is the default value) means all the object types. object_types = "all" # The max retained audit log files. Defaults to 30. # A audit log is rotated daily. max_log_files = 30 ``` ## Caveats Audit logs can be huge if not properly configured. For example, in a busy loaded GreptimeDB, setting "`all`" to all the `sources`, `classes`, `commands` and `object_types` will record all the statements executed on GreptimeDB, resulting in a very large audit log file. That could easily explode the disk space. So, it's highly recommended to configure the audit log plugin properly to avoid such a situation. --- ## Check GreptimeDB Status(Monitoring) Please refer to the [OSS GreptimeDB documentation](/user-guide/deployments-administration/monitoring/check-db-status.md) for details on how to check the health status of GreptimeDB. --- ## Key Logs(Monitoring) During operation, GreptimeDB outputs key operations and unexpected error information to logs. You can use these logs to understand GreptimeDB's operational status and troubleshoot errors. ## GreptimeDB Operational Logs Refer to the [important logs](/user-guide/deployments-administration/monitoring/key-logs.md) outlined in the GreptimeDB OSS documentation for recommended monitoring practices. ## License Expiration Logs For the Enterprise Edition of GreptimeDB, it is crucial to monitor license expiration logs to ensure uninterrupted access to enterprise features. When the license expires, the metasrv component will generate the following warning log. Promptly contact GreptimeDB to renew your license: ```bash License is expired at xxx, please contact your GreptimeDB vendor to renew it ``` --- ## Key Metrics The key metrics for monitoring include CPU, memory, disk I/O, and network bandwidth usage. ## Alert Metrics Since each company typically has its own alerting system with potentially different configuration methods, this document does not provide specific alert configuration procedures. Instead, it lists key metrics that should be monitored. You can configure alerts based on whether these metrics remain at abnormal values for extended periods (several minutes). You should set alert thresholds according to your specific circumstances. | Metric | Description | Reference Rule | | --- | --- | --- | | `sum(process_resident_memory_bytes{}) by (instance, pod)` | Process memory usage | Usage rate continuously exceeds threshold | | `sum(rate(process_cpu_seconds_total{}[$__rate_interval]) * 1000) by (instance, pod)` | Process CPU usage, displayed in millicores | Utilization rate continuously exceeds threshold | | `sum by(instance, pod) (greptime_mito_write_stall_total{instance=~"$datanode"})` | Number of backlogged write requests on datanode | Remains greater than 0 for n minutes | | `sum(rate(greptime_table_operator_ingest_rows{instance=~"$frontend"}[$__rate_interval]))` | Current rows written per second | Drops to 0 (or below threshold) for n minutes | | `greptime_mito_compaction_failure_total` | Compaction failures | Recent increase greater than 0 | | `greptime_mito_flush_failure_total` | Flush failures | Recent increase greater than 0 | | `sum by (instance, pod, region_id) (rate(greptime_mito_region_query_cpu_time{instance=~"$frontend"}[$__rate_interval]))` | Per-region query CPU time in nanoseconds | Use for read-load hotspot analysis after enabling `logging.enable_per_region_metrics`; avoid broad alerting because this metric has high cardinality | | `sum by (instance, pod, region_id) (rate(greptime_mito_region_query_scanned_bytes{instance=~"$frontend"}[$__rate_interval]))` | Per-region scanned bytes during queries | Use for read-load hotspot analysis after enabling `logging.enable_per_region_metrics`; avoid broad alerting because this metric has high cardinality | | `sum by(instance, pod, path, method, code) (rate(greptime_servers_http_requests_elapsed_count{path!~"/health\|/metrics"}[$__rate_interval]))` | HTTP request count and response codes | HTTP 200 request count below threshold for n minutes or non-200 response count above normal threshold for n minutes | | `sum by (instance, pod) (rate(greptime_mito_write_rows_total{instance=~"$datanode"}[$__rate_interval]))` | Storage engine write row count | Below normal threshold for n minutes | We also recommend configuring disk alerts at the Pod level to trigger when disk usage exceeds a certain threshold. Additionally, you can monitor the following events based on keywords from error logs listed in the operational [key logs](key-logs.md): - Region lease renewal failures - Region failover --- ## Monitoring(Monitoring) Effective database management relies heavily on monitoring. You can monitor GreptimeDB using the following methods: --- ## Deployments & Administration(Deployments-administration) Before reading this document, it is recommended to review the [Deployments & Administration documentation](/user-guide/deployments-administration/overview.md) of the GreptimeDB open-source database version, which covers all features that are also available in the enterprise edition. The following sections describe the enterprise-specific features and capabilities. ## Configuration and Deployment - [Deploy GreptimeDB Enterprise on Kubernetes](./deploy-on-kubernetes/overview.md): Learn how to obtain the dedicated GreptimeDB Enterprise image. - [Standalone Leader-Follower Mode](./standalone-follower.md): Run standalone nodes with leader election, automatic promotion, and optional follower reads. - [LDAP Authentication](authentication.md): Enhanced authentication capabilities for GreptimeDB Enterprise. ## Monitoring The [monitoring](./monitoring/overview.md) section describes the enterprise version specific metrics and logs. ## Disaster Recovery The [disaster recovery](./disaster-recovery/overview.md) section provides strategies and best practices for ensuring data durability and availability in GreptimeDB Enterprise. --- ## Query Guard Query Guard is a GreptimeDB Enterprise plugin that intercepts queries at the frontend protocol layer and rejects potentially dangerous statements before they are executed. It provides the following protections: - **Ban `DROP TABLE`**: reject all `DROP TABLE` statements. - **Ban `DROP DATABASE`**: reject all `DROP DATABASE` statements. - **Ban `TRUNCATE TABLE`**: reject all `TRUNCATE TABLE` statements. - **Reject `COPY` statements**: reject all `COPY` statements. - **Disallow cross-catalog access**: reject queries that reference tables across different catalogs, cross-catalog gRPC DDL requests, and Flight bulk inserts targeting a different catalog. ## Overview The configured operation bans are enforced in the query interceptors, which run **before any permission check**. This means the bans apply to **every user, including administrators**. No one can run a banned operation through a configured frontend until the configuration is changed and the frontend is restarted. The bans cover both the SQL protocols (MySQL, PostgreSQL, and HTTP) and the gRPC protocol: - SQL path: configured `DROP TABLE`, `DROP DATABASE`, and `TRUNCATE TABLE` statements are rejected with a `NotSupported` error. - gRPC path: configured structured `DROP TABLE` and `TRUNCATE TABLE` DDL requests are rejected. The structured gRPC DDL request has no drop-database variant; however, SQL statements sent over gRPC go through the same SQL interceptors, so the `DROP DATABASE` ban applies to SQL over gRPC as well. Internal operations such as TTL-based data expiration and automatic cleanup bypass the frontend protocol-layer interceptors and are **not** affected by these bans. ## Configuration Query Guard is provided as a plugin in GreptimeDB. To enable and configure it, add the following TOML to your GreptimeDB config file: ```toml [[plugins]] # Add the query guard plugin to your GreptimeDB. [plugins.query_guard] # Whether to enable the query guard plugin, defaults to false. enable = true # Operations to ban for all users. The list is empty by default. banned_ops = ["drop_table", "drop_database", "truncate_table"] ``` The supported operation names are `drop_table`, `drop_database`, and `truncate_table`. To ban only a subset of these operations, include only their names in `banned_ops`. The former `ban_drop_table`, `ban_drop_database`, and `ban_truncate_table` options are no longer supported. Using any of them causes configuration parsing to fail. The plugin works in both standalone mode and distributed mode. In distributed mode, it takes effect on the frontend where it is configured. ## Caveats - **Every frontend must carry the configuration.** In a deployment with multiple frontends, you must add the plugin configuration to every frontend's config file; frontends without the configuration will still execute operations listed in `banned_ops`. - **Operation bans must be configured separately.** Turning on `query_guard` automatically rejects `COPY` statements, cross-catalog queries, cross-catalog gRPC DDL requests, and Flight bulk inserts targeting a different catalog. To ban the supported operations, add them to `banned_ops`, which is empty by default. - The bans are enforced at the protocol layer. Changing the configuration requires a restart of the frontend to take effect. --- ## Scheduled Compaction Scheduled Compaction is a GreptimeDB Enterprise feature that periodically submits regular compaction requests for all physical Regions that have a leader in a cluster. It runs on the leader Metasrv, uses a cron expression and an IANA time zone to determine the schedule, and stores recent job reports in the Metasrv KV backend. This feature complements GreptimeDB's automatic and [manual compaction](/user-guide/deployments-administration/manage-data/compaction.md#strict-window-compaction-strategy-swcs-and-manual-compaction). Use it when you want to compact all Regions regularly during a predictable maintenance window. ## Configure the plugin Add the `compaction_cronjob` plugin to the Metasrv configuration: ```toml [[plugins]] [plugins.compaction_cronjob] enable = true timezone = "Asia/Shanghai" cron = "0 0 0 * * *" max_concurrent_regions = 4 compact_parallelism = 1 manual_trigger_cooldown_secs = 3600 max_history_jobs = 100 ``` The cron expression includes seconds. For example, `0 0 0 * * *` runs at midnight every day in the configured time zone. | Option | Default | Description | | --- | --- | --- | | `enable` | `false` | Enables scheduled compaction. | | `timezone` | Local IANA time zone, or `UTC` if it cannot be detected | The time zone used to evaluate `cron`, for example `Asia/Shanghai`. | | `cron` | `0 0 0 * * *` | The compaction schedule. | | `max_concurrent_regions` | `4` | The maximum number of Region compaction requests submitted concurrently by one job. Must be greater than `0`. | | `compact_parallelism` | `1` | The compaction parallelism passed to each Region request. Must be greater than `0`. | | `manual_trigger_cooldown_secs` | `3600` | The minimum interval between a manual trigger and the most recent job, whether that job was scheduled or manually triggered. Set it to `0` to disable the cooldown. Scheduled jobs are not delayed by this option. | | `max_history_jobs` | `100` | The maximum number of terminal job reports retained after history pruning runs when a job completes. Must be greater than `0`. | Only the leader Metasrv runs the scheduler. After leadership changes, the new leader continues scheduling future jobs from the configured cron expression. Before starting the scheduler, a new leader marks jobs left in `running` state as `cancelled` and writes a warning log. This status change does not cancel Region requests that were already submitted by the previous leader. Cancelled jobs are terminal and participate in history pruning the next time a job completes. ## HTTP endpoints The plugin adds the following endpoints to the Metasrv HTTP server. Replace `` with the configured Metasrv HTTP address. ### Trigger a job ```bash curl -X POST \ "http:///admin/compaction_cronjob/trigger" ``` Send this request to the leader Metasrv. The request waits for the job to finish submitting Region compaction requests and returns its report. - Returns `409 Conflict` when sent to a follower Metasrv. - Returns `429 Too Many Requests` when the manual trigger is still within `manual_trigger_cooldown_secs` of the most recent scheduled or manual job. - Returns `500 Internal Server Error` for other failures. ### Get a job ```bash curl --get \ --data-urlencode "job_id=2026-06-16T00:00:00+00:00" \ "http:///admin/compaction_cronjob/job" ``` The `job_id` is returned by the trigger and job-list endpoints. It is an RFC 3339 timestamp and should be URL encoded. The endpoint returns `404 Not Found` when the job does not exist. ### List recent jobs ```bash curl --get \ --data-urlencode "limit=20" \ "http:///admin/compaction_cronjob/jobs" ``` Jobs are ordered by scheduled time in descending order. The optional `limit` parameter defaults to `20`. ## Job report The trigger and query endpoints return JSON job reports. A completed successful job resembles: ```json { "job_id": "2026-06-16T00:00:00+00:00", "scheduled_at": "2026-06-16T00:00:00Z", "started_at": "2026-06-16T00:00:00.010Z", "finished_at": "2026-06-16T00:00:01.010Z", "status": "succeeded", "execution_result": { "total_regions": 8, "submitted_regions": 8, "failed_regions": [] } } ``` The `status` field is `"running"`, `"succeeded"`, `"partial_failed"`, or `"cancelled"`. A failed job uses `{"failed": ""}` as the `status` value. The execution result records how many Region requests were attempted and submitted, together with Region-level submission failures. --- ## Standalone Leader-Follower Mode GreptimeDB Enterprise supports running standalone nodes in leader-follower mode. The nodes share an external metadata store and data storage. One node is elected leader and accepts writes; the other stays running as a follower and can become leader after a failover. You can also enable reads on the follower for workloads that tolerate stale data. This topology differs from [active-active failover](/enterprise/deployments-administration/disaster-recovery/dr-solution-based-on-active-active-failover.md), where both standalone peers accept writes and replicate changes to each other. It also differs from [cluster Read Replicas](/enterprise/read-replicas/overview.md), which manage follower Regions across Datanodes. ## Choose a Deployment Mode Use the following comparison to choose between standalone leader-follower mode, [active-active failover](/enterprise/deployments-administration/disaster-recovery/dr-solution-based-on-active-active-failover.md), and a [distributed cluster](/user-guide/concepts/architecture.md). | Aspect | Standalone leader-follower mode | Active-active failover | Distributed cluster | | --- | --- | --- | --- | | Writes and queries | One elected leader accepts writes. Optional follower reads can return stale data. | Both peers accept writes and execute queries locally; changes replicate asynchronously. | Frontends route writes to Region leaders and distribute queries across Datanodes. | | State and dependencies | Shared PostgreSQL or MySQL metadata and shared data storage; a load balancer routes traffic to the leader. The example below uses a local WAL on each node. | Each peer keeps its own complete data copy and pending replication changes. An external mechanism routes traffic. | Separate Frontend, Metasrv, and Datanode services, plus a metadata backend, data storage, and the selected WAL backend. | | Failover | Election promotes a follower; the load balancer redirects new connections once the new leader is ready. | An external traffic mechanism switches to an available peer; the topology does not elect a primary. | Configured Region Failover can reopen Regions on surviving Datanodes. Availability depends on WAL, shared storage, and spare capacity. | | Advantages | Automatic leader promotion with fewer separately deployed GreptimeDB components than a distributed cluster. | Peers can continue serving independently during connectivity interruptions and synchronize after recovery. | Horizontal scaling, distributed query execution, and independent scaling of service components. | | Limitations | Write capacity remains bounded by one leader. Both nodes depend on shared services, and the local-WAL configuration can lose unflushed writes after a node and its WAL are lost. | Replication lag can leave a peer missing recent writes. Independent writes and Schema changes require reconciliation planning; this is not distributed query scale-out. | More components to deploy and operate. Failover and durability require appropriate configuration rather than following automatically from cluster mode. | Prefer leader-follower mode when the write workload fits on one standalone node, you want automatic leader promotion without separately deploying cluster components, and you can provide highly available shared metadata and data storage. It suits applications that can reconnect after failover and accept the durability limits of their chosen WAL configuration. Prefer active-active failover when you need independently writable peers that can continue serving during inter-node connectivity interruptions. Choose a distributed cluster when you need to spread writes or query work across multiple Datanodes. Leader election selects the writable node; the load balancer and clients still handle traffic switching and reconnection. ## System Architecture Place a load balancer in front of the standalone nodes to provide a stable application endpoint. It must check each node's `/status/standalone/is_leader` HTTP endpoint and route application traffic only to the node returning `200 OK`. A follower or a node preparing leadership returns `503 Service Unavailable` and must not receive traffic through this endpoint. ```mermaid flowchart TB Clients[Application clients] -->|MySQL, PostgreSQL, HTTP, or gRPC| LB[Load balancer] LB -->|Application traffic| A[Node A: ready leader] LB -.->|Leader check: 200| A LB -.->|Leader check: 503| B[Node B: follower] A <--> Metadata[Shared PostgreSQL or MySQL metadata and election] B <--> Metadata A <-->|Read and write| Storage[Shared data storage] Storage -->|Read-only access| B ``` The diagram shows node A as the current leader. When node B becomes the ready leader, the load balancer must select node B for new connections and stop sending new connections to node A. Configure listeners for the application protocols you use, with the HTTP leader check selecting eligible backends for each listener. Do not use round-robin routing across both nodes based only on process health. If you enable follower reads, use a separate read-only route for workloads that tolerate stale data. Keep the main application endpoint directed at the ready leader. ## How It Works When election is enabled, each node starts its services as a follower and participates in leader election through the shared PostgreSQL or MySQL metadata store. The elected node prepares its local Regions and services before accepting writes. | Role | Behavior | | --- | --- | | `follower` | Rejects writes and schema changes. Can refresh local read-only Regions when follower reads are enabled. | | `leader_preparing` | Has won election but is still preparing to serve traffic. Rejects public writes. | | `leader` | Is ready to serve reads, writes, and schema changes. | Roles are assigned by election, so either node can become leader. The load balancer follows these role changes to direct new connections to the ready leader; clients must reconnect when an existing connection still points to a former leader. ## Prerequisites For a deployment on two separate hosts, prepare: - A GreptimeDB Enterprise build with standalone leader-follower mode support and the PostgreSQL or MySQL metadata backend you intend to use. - A load balancer in front of both nodes, configured to route application traffic using the HTTP leader check. - A shared PostgreSQL or MySQL database for metadata and election. The local `raft_engine` **metadata backend** does not support election. - Shared data storage, such as an S3 bucket and root prefix, accessible from both nodes. Separate local data directories alone do not provide a shared data copy. - A unique, reachable gRPC address for each node. Followers use the leader's advertised address to check for data updates when follower reads are enabled. - Local directories for each node's WAL, logs, and caches, plus the authentication and licensing configuration for your Enterprise deployment. The shared metadata database and data storage are dependencies of both nodes. Plan their availability along with the standalone nodes. ## Configure the Nodes The following example configures node A in a new two-host deployment with PostgreSQL metadata and S3 data storage. Replace the addresses, credentials, bucket, and paths for your environment. See [storage options](/user-guide/deployments-administration/configuration.md#storage-options) for other storage settings. ```toml title="standalone.toml" [http] addr = "0.0.0.0:4000" [grpc] bind_addr = "0.0.0.0:4001" server_addr = "10.0.0.1:4001" [wal] provider = "raft_engine" dir = "/var/lib/greptimedb/wal" [storage] data_home = "/var/lib/greptimedb" type = "S3" bucket = "" root = "standalone-ha" region = "" access_key_id = "" secret_access_key = "" [enterprise_standalone.metadata_store] backend = "postgres" store_addrs = ["postgres://:@:5432/greptime_meta"] table_name = "greptime_metakv" [enterprise_standalone.metadata_store.election] enabled = true server_addr = "10.0.0.1:4001" store_key_prefix = "standalone-ha" meta_election_lock_id = 1 # Optional: refresh read-only Regions while this node is a follower. enable_follower_read = true ``` On node B, use the same metadata database, metadata table, election prefix, PostgreSQL election lock ID, and S3 bucket/root. Change both `server_addr` values to node B's reachable gRPC address, for example `10.0.0.2:4001`. The local directory paths can be identical on separate hosts, but each node must use its own local WAL directory. For MySQL metadata, set `backend = "mysql"` and use a MySQL connection URL in `store_addrs`. The election table defaults to `_election`; if you set `mysql_election_table_name` in the election section, use the same value on both nodes. `meta_election_lock_id` applies to PostgreSQL. Start each node with its configuration and your deployment's authentication and licensing options: ```shell greptime-ee standalone start -c standalone.toml ``` ### Election Options These options belong to `[enterprise_standalone.metadata_store.election]`: | Option | Default | Purpose | | --- | --- | --- | | `enabled` | `false` | Enable leader election. Set to `true` on both nodes. | | `enable_follower_read` | `false` | Enable periodic follower Region discovery and read refresh. Requires election to be enabled. | | `server_addr` | Derived from the standalone gRPC advertised address | Identify this participant and advertise the address used for leader RPCs. Use a unique, reachable address for each node. | | `store_key_prefix` | Empty string | Namespace election records. Use the same prefix within a deployment. | | `meta_election_lock_id` | `1` | PostgreSQL election lock ID. Use the same ID within a deployment. | | `candidate_lease_secs` | `600` | Candidate registration lease duration in seconds. Must be greater than zero. | | `meta_lease_secs` | `5` | Leader lease duration in seconds; also controls follower metadata polling. Must be greater than zero. | | `mysql_election_table_name` | `_election` | MySQL election table name. | ## Follower Reads and Freshness Follower read refresh is opt-in. With `enable_follower_read = false`, a follower still participates in election, rejects writes, and can be promoted, but it does not periodically discover or catch up Regions for query traffic. With `enable_follower_read = true`, the follower discovers new, changed, or removed Regions from shared metadata and updates its local read-only Regions. Applications can send queries directly to that follower's SQL or HTTP endpoint. Writes and schema changes must still go to the leader. Follower queries read data published to shared storage. They do **not** fetch the leader's unflushed memtable data. Read-after-write workloads should query the leader. Metadata polling uses `meta_lease_secs` (5 seconds by default). Existing Regions are checked for data updates on a separate 30-second catch-up cadence. Visibility also depends on leader flushes, storage access, and refresh completion, so these intervals are not a maximum staleness guarantee. If the leader is unavailable or not ready, the follower retains its current local Regions and retries catch-up later. For a simple verification, run the following on the leader: ```sql CREATE TABLE follower_demo ( ts TIMESTAMP TIME INDEX, value DOUBLE ); INSERT INTO follower_demo VALUES ('2026-01-01 00:00:00', 1.0); ADMIN FLUSH_TABLE('follower_demo'); ``` Then query the follower, allowing time for metadata discovery and data refresh: ```sql SELECT * FROM follower_demo; ``` The explicit flush publishes data for this check; it does not make follower reads synchronous. ## Check Roles and Route Traffic Query a node's HTTP endpoint to inspect its role: ```shell curl http://10.0.0.2:4000/status/standalone/role ``` Example follower response: ```json { "role": "follower", "is_leader": false, "leader_addr": "10.0.0.1:4001" } ``` `leader_addr` is the leader's advertised gRPC address, or `null` when the follower does not know it. A node in `leader_preparing` also reports `is_leader: false`. For load balancer checks that select the write endpoint, use: ```shell curl -i http://10.0.0.1:4000/status/standalone/is_leader ``` - `200 OK`: the node is the ready leader. - `503 Service Unavailable`: the node is a follower or is still preparing leadership. The general `/health` endpoint checks process health; it does not establish that the node can accept writes. Configure write routing to use the leader check. If you enable follower queries, route those separately and allow for stale results. :::warning Persistent connections after a leader steps down A persistent MySQL connection or gRPC channel can remain connected to the original leader after it steps down. The connection may still be open, but the node is now a follower and rejects subsequent writes. Updating the load balancer's backend selection does not migrate established connections to the new leader. Clients and connection pools must handle write rejections, close or replace connections to the former leader, and reconnect through the load balancer after it identifies the new ready leader. Retrying writes on the same connection can continue to fail. ::: ## Failover and Durability When the leader loses leadership or stops, a follower can win election. It reconciles its local state with shared metadata and prepares its services before reporting `leader` and returning `200` from the leader check. The load balancer then routes new connections to this node. Clients must reconnect through the load balancer; election does not move existing client connections. Failover time includes election, leader preparation, traffic health checks, and client reconnection. The lease duration alone is not an RTO guarantee. The example above uses a local WAL on each node. A follower does not replicate the other node's local WAL or unflushed memtables. Writes that exist only in the old leader's local WAL are unavailable to the new leader until recovered and published to shared storage; losing that WAL before publication can lose those writes. This configuration therefore does not guarantee zero RPO. See the [WAL overview](/user-guide/deployments-administration/wal/overview.md) when planning durability requirements. Before using the deployment for application traffic, verify that only one node passes the leader check, the follower rejects writes, and follower queries work if enabled. Then stop the leader, wait for the other node to pass the leader check, and verify reads and writes through the application's normal endpoint. --- ## Aggregation Queries ## Metric Aggregations In GreptimeDB, metric aggregations are used to perform statistical calculations on numeric fields, such as sum, average, maximum, minimum, etc. | Aggregation | Status | Description | | ----------- | ------ | ------------------------------------------------------------ | | sum | | Aggregates to calculate the sum of numeric fields. | | avg | | Aggregates to calculate the average of numeric fields. | | max | | Aggregates to calculate the maximum value of numeric fields. | | min | | Aggregates to calculate the minimum value of numeric fields. | | count | ✅ | Aggregates to calculate the number of documents. | ## Bucket Aggregations In GreptimeDB, bucket aggregations are used to group documents, such as bucketing by time, category, or other fields. | Aggregation | Status | Description | | -------------- | ------ | ------------------------------------------ | | date_histogram | ✅ | Aggregates to bucket by time fields. | | terms | | Aggregates to group by values of specified | --- ## GreptimeDB Elasticsearch Compatible Overview # Overview The Elasticsearch compatibility layer in GreptimeDB is designed to provide a certain degree of compatibility with Elasticsearch, allowing users to migrate existing Elasticsearch applications to GreptimeDB with relative ease. However, GreptimeDB does not fully support all features and functionalities of Elasticsearch. In some cases, users may need to adjust or modify their applications to achieve the same functionality in GreptimeDB. ## Principle Essentially, GreptimeDB receives Elasticsearch QueryDSL syntax, converts it to GreptimeDB's query syntax, and returns data in the Elasticsearch API format to achieve compatibility. ## Supported API List | API | Method | Description | | ------------------------------ | ------ | -------------------- | | /\{table_name\}/\_search | POST | Execute search query | | /\{table_name\}/\_async_search | POST | Execute search query | | /\_resolve/index/\{schema_name\} | POST | Index document | | /\{table_name\}/\_field_caps | GET | Get field info | ## Supported Query List | Query | Description | | ------------ | ----------------- | | match | Match query | | match_phrase | Phrase match | | match_all | Match all | | term | Exact match | | prefix | Prefix match | | range | Range query | | exists | Field existence | | bool | Compound query | | aggregation | Aggregation query | ## Supported Aggregation List | Aggregation | Status | Description | | -------------- | ------ | -------------- | | avg | | Average value | | sum | | Sum | | min | | Minimum value | | max | | Maximum value | | count | ✅ | Count | | date_histogram | ✅ | Date histogram | | histogram | | Histogram | | terms | | Terms | ## Querying GreptimeDB with Kibana For more information, please contact us. --- ## Query Since GreptimeDB and Elasticsearch are designed for different purposes, many features are not compatible. We have provided corresponding alternatives as much as possible. For each specific interface, we will explain GreptimeDB's implementation and the differences from Elasticsearch. :::tip All features related to boost scores are not supported. The `_id` field is not automatically added or maintained. If you need to use it, please include this field in your original data. Using @timestamp as a time field is not supported. It is not allowed to include both query and aggregation in the same request. ::: ## Query Syntax ### match In GreptimeDB, the `match` query is used to match one or more terms in a text field. The following options are not supported: analyzer, auto_generate_synonyms_phrase_query, fuzziness, max_expansions, prefix_length, fuzzy_transpositions, fuzzy_rewrite, lenient, operator, minimum_should_match, zero_terms_query. ### match_phrase In GreptimeDB, the `match_phrase` query is used to match a phrase in a text field. Different queries are generated based on the data type. If the data type is text, it is converted to a LIKE query. For other data types, it is converted to an equivalent value query. The following options are not supported: analyzer, auto_generate_synonyms_phrase_query, fuzziness, max_expansions, prefix_length, fuzzy_transpositions, fuzzy_rewrite, lenient, operator, minimum_should_match, zero_terms_query. ### match_all In GreptimeDB, the `match_all` query is used to match all documents. ### term In GreptimeDB, the `term` query is used to match an exact value in a text field. The query is mainly converted to an equality query. Currently, case_insensitive is not supported. ### prefix In GreptimeDB, the `prefix` query is used to match a prefix in a text field. The query is mainly converted to a LIKE query. For example, if you query for text starting with `yi`, it will be converted to `LIKE 'yi%'`. The following options are not supported: rewrite, case_insensitive. ### range In GreptimeDB, the `range` query is used to match a range in a text field. The query is mainly converted to greater than or equal to and less than or equal to conditions. Currently, format, relation, time_zone, and boost are not supported. ### exists In GreptimeDB, the `exists` query is used to match values that exist in a text field. The query is mainly converted to an existence condition. ### bool In GreptimeDB, the `bool` query is used to match multiple query conditions. The query is mainly converted to a combination of AND and OR conditions. --- ## Iceberg Export GreptimeDB Enterprise can expose its tables through an [Apache Iceberg](https://iceberg.apache.org/) REST catalog, so external query engines — pyiceberg, Spark, Trino, DuckDB, and any other Iceberg-compatible client — can read GreptimeDB data directly from object storage using standard Iceberg APIs. This is a **read-only export**, not a copy or a dual-write. GreptimeDB keeps writing data the way it always has; the Iceberg integration simply publishes the metadata that lets other engines find and interpret those same files. ## How it works The key idea is that GreptimeDB already stores its SST (sorted string table) data files as Parquet in object storage. The Iceberg integration does **not** re-write, duplicate, or export the data. Instead it publishes Iceberg **metadata** — manifests, manifest-lists, and table-metadata snapshots — that point at the existing Parquet files. Any engine that speaks Iceberg can then read those files through the REST catalog. The following diagram shows how the pieces fit together: ```mermaid flowchart LR APP["Client applications(MySQL / PostgreSQL protocol)"] subgraph GT["GreptimeDB Enterprise"] direction TB FE["FrontendIceberg REST catalog (/v1/iceberg)"] DN["Datanode / standalone(the writer)"] end subgraph OS["Object storage(S3 / OSS / GCS / Azure Blob)"] DATA["Parquet SST data files"] META["Iceberg metadata(manifests, snapshots)under warehouse_root"] end ENG["pyiceberg / Spark / Trino / DuckDB(any Iceberg-compatible engine)"] APP -->|"writes"| FE FE --> DN DN -->|"writes SST files onflush / compaction"| DATA DN -->|"publishes metadata,commits a new snapshot"| META ENG -->|"1. load table metadata"| FE ENG -->|"2. read Parquet files directly"| DATA META -.->|"points at"| DATA ``` This is split across the GreptimeDB processes you already run: - **Datanode / standalone (the writer).** Whenever SST files are written — on flush, compaction, bulk ingestion, and truncate — GreptimeDB translates the live set of Parquet files into Iceberg manifest entries and commits a new Iceberg snapshot. The Iceberg metadata is written under a `warehouse_root` prefix inside the same object-storage bucket the datanode already uses. - **Frontend (the catalog server).** It implements the [Iceberg REST Catalog API](https://iceberg.apache.org/docs/1.6.0/api/#rest-catalog-specification), mounted at `/v1/iceberg`, and serves each table's current metadata to clients by reading it **directly from object storage** — see [Configuration](#configuration) for the object-storage settings the frontend therefore needs. Because the data files are never duplicated, there is no extra storage cost and no write-path duplication — the export is pure metadata laid down beside the data GreptimeDB already writes. ### How GreptimeDB concepts map to Iceberg | GreptimeDB | Iceberg | | ---------- | ------- | | Schema (database, e.g. `public`) | Namespace | | Table | Table | | Column | Field | | Time index column | A `timestamp` field | Schema changes from `ALTER TABLE` (added / renamed / dropped columns) are reflected in the Iceberg schema without any restart. ### Type mapping GreptimeDB column types map to Iceberg types as follows: | GreptimeDB type | Iceberg type | | --------------- | ------------ | | `boolean` | `boolean` | | `int8`, `int16`, `int32` | `int` | | `int64` | `long` | | `float32` | `float` | | `float64` | `double` | | `string` | `string` | | `binary` | `binary` | | `date` | `date` | | `timestamp` (any precision) | `timestamp` | | Prometheus native histogram (struct) | `struct` (with `list` sub-fields) | | `uint8`, `uint16`, `uint32`, `uint64` | `long` — **not readable in Spark** (see below) | | `list`, `dictionary`, `json`, `interval`, `duration`, `time`, arbitrary `struct` | `string` (lossy fallback) | ## Configuration The Iceberg integration is an enterprise plugin. Enable it by adding an `iceberg_manifest` entry to the `[[plugins]]` section of **both** the data-writing process (datanode or standalone) **and** the frontend: - The **datanode / standalone** runs the writer hook that publishes Iceberg metadata. - The **frontend** runs the REST catalog that serves it. Both must reference the **same** `warehouse_root` so the catalog reads exactly the metadata the writer publishes. **Datanode / standalone.** The process already has a `[storage]` section for its data — leave it as is and add the plugin entry: ```toml ## The object storage the datanode already uses (existing configuration). [storage] type = "S3" bucket = "greptimedb" root = "greptimedb" endpoint = "https://s3.us-east-1.amazonaws.com" region = "us-east-1" access_key_id = "" secret_access_key = "" ## Iceberg manifest export publishes Iceberg-format metadata (snapshots, ## manifests, manifest-lists) so external engines (pyiceberg, Spark, Trino, ## DuckDB, ...) can read GreptimeDB tables through the Iceberg REST catalog. [[plugins]] iceberg_manifest = { warehouse_root = "iceberg_warehouse" } ``` **Frontend.** The frontend needs the same object-storage configuration as the datanode. The REST catalog runs inside the frontend and reads the Iceberg metadata **directly from object storage** — it does not fetch it through the datanode — so without a `[storage]` section the catalog cannot resolve any table. A frontend normally has no `[storage]` section of its own (it only proxies queries); enabling the Iceberg integration is the reason it needs one. Configure the **same bucket, `root`, and credentials** as the datanode, plus the plugin entry: ```toml ## The SAME object storage as the datanode — the frontend's REST catalog ## reads the Iceberg metadata from here. [storage] type = "S3" bucket = "greptimedb" root = "greptimedb" endpoint = "https://s3.us-east-1.amazonaws.com" region = "us-east-1" access_key_id = "" secret_access_key = "" [[plugins]] iceberg_manifest = { warehouse_root = "iceberg_warehouse" } ``` :::note Deploying with the greptimedb-cluster Helm chart You don't hand-write the frontend's `[storage]` section on Kubernetes. The chart configures object storage centrally through the `objectStorage` values, and by default only the datanode receives that configuration. Set `frontend.enableObjectStorage` to `true` so the frontend is given the same object-storage configuration, and add the plugin entry through `frontend.configData`: ```yaml frontend: ## Inject the objectStorage values (bucket, root, credentials, ...) ## into the frontend as its [storage] section. enableObjectStorage: true configData: |- [[plugins]] iceberg_manifest = { warehouse_root = "iceberg_warehouse" } ``` See the [Helm chart configurations](/user-guide/deployments-administration/deploy-on-kubernetes/common-helm-chart-configurations.md) for the `objectStorage` and `configData` fields. ::: The plugin options are: | Option | Default | Description | | ------ | ------- | ----------- | | `warehouse_root` | `"iceberg_warehouse"` | Path prefix, inside the datanode's object-storage bucket, where Iceberg metadata is stored. | | `enable_incremental` | `true` | When `true` (the default), a new snapshot is published automatically on every flush / compaction / truncate. Set `false` to disable automatic publication and generate metadata on demand through the rebuild interface instead; drop/GC cleanup still runs. | The `warehouse_root` is a path prefix inside the object-storage bucket described above, folded into the store's `root` (e.g. `s3://///`) — which is why the frontend and the datanode must agree on both the `[storage]` configuration and the `warehouse_root`, or the catalog will look for metadata that is not there. The supported object-storage backends are S3, OSS, GCS, and Azure Blob. Once enabled, the REST catalog is available at: ``` http://:/v1/iceberg ``` For example, with a frontend listening on the default HTTP port `4000`, the catalog base URI is `http://localhost:4000/v1/iceberg` and the warehouse name is `greptime` (the default). ## Make a table readable GreptimeDB publishes Iceberg metadata on flush, so the export of recently written data appears after the next flush (or compaction). To expose freshly-ingested rows immediately, flush the table manually: ```sql -- via the MySQL or PostgreSQL protocol admin flush_table('your_table'); ``` The metadata is published asynchronously; the table becomes queryable through the REST catalog shortly after the flush returns. :::note Pre-existing tables are not exported automatically Automatic publication only covers manifest updates that happen **after** the plugin is enabled (flush, compaction, truncate) — there is no startup bootstrap that publishes SST files flushed earlier. A table whose data was already flushed **before** the integration was enabled therefore remains absent from the catalog until its next flush or compaction. To export such pre-existing tables, run the rebuild endpoint once per table: ```bash curl -X POST \ "http://localhost:4000/v1/iceberg/v1/greptime/namespaces/public/tables//rebuild" ``` See [Rebuild / reconcile](#operational-notes) below for the details and caveats of a rebuild. ::: ## Reading with pyiceberg This example uses [pyiceberg](https://py.iceberg.apache.org/) with pyarrow to read a GreptimeDB table through the REST catalog. You will need `pyiceberg` and `pyarrow` installed, plus credentials for the object storage that backs your GreptimeDB deployment (S3 is shown here). ```python from pyiceberg.catalog.rest import RestCatalog # The REST catalog endpoint exposed by the GreptimeDB frontend. # `warehouse` is the catalog name (the default catalog is "greptime"). catalog = RestCatalog( name="greptime", uri="http://localhost:4000/v1/iceberg", prefix="greptime", # Object-storage credentials so pyiceberg can read the Parquet data files. # Match the backend your GreptimeDB deployment uses. **{ "s3.endpoint": "https://s3.us-east-1.amazonaws.com", "s3.access-key-id": "YOUR_ACCESS_KEY", "s3.secret-access-key": "YOUR_SECRET_KEY", "s3.region": "us-east-1", }, ) # List namespaces (= GreptimeDB schemas) and tables. print(catalog.list_namespaces()) # e.g. [('public',)] print(catalog.list_tables("public")) # e.g. [('public', 'my_table')] # Load a table and scan it with pyarrow. table = catalog.load_table(("public", "my_table")) print(table.schema()) # the Iceberg schema (GreptimeDB columns → fields) arrow_table = table.scan().to_arrow() print(arrow_table.num_rows, "rows") print(arrow_table.to_pandas().head()) ``` ## Reading with Spark This example configures Spark SQL to use the GreptimeDB Iceberg REST catalog and run queries over a GreptimeDB table. You need the Iceberg Spark runtime and the AWS bundle JARs on the Spark classpath (versions must match your Spark/Scala version — Spark 4.x with Iceberg 1.11.0 is shown). **1. `spark-defaults.conf`** ```properties spark.sql.extensions org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions spark.sql.defaultCatalog greptime spark.sql.catalog.greptime org.apache.iceberg.spark.SparkCatalog spark.sql.catalog.greptime.type rest spark.sql.catalog.greptime.uri http://localhost:4000/v1/iceberg spark.sql.catalog.greptime.warehouse greptime # S3FileIO reads the Parquet data files from the same object storage GreptimeDB uses. spark.sql.catalog.greptime.io-impl org.apache.iceberg.aws.s3.S3FileIO spark.sql.catalog.greptime.client.region us-east-1 spark.sql.catalog.greptime.s3.endpoint https://s3.us-east-1.amazonaws.com spark.sql.catalog.greptime.s3.path-style-access false spark.sql.catalog.greptime.s3.access-key-id YOUR_ACCESS_KEY spark.sql.catalog.greptime.s3.secret-access-key YOUR_SECRET_KEY ``` Start `spark-sql` (or a `spark-submit` job) with the two Iceberg JARs on the classpath: ```bash spark-sql \ --conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \ --jars iceberg-spark-runtime-4.1_2.13-1.11.0.jar,iceberg-aws-bundle-1.11.0.jar ``` For an S3-compatible store with a custom endpoint and path-style addressing (for example MinIO, Garage, or a local test deployment), set `spark.sql.catalog.greptime.s3.endpoint` to the store URL and `spark.sql.catalog.greptime.s3.path-style-access true`. **2. Create and populate a table in GreptimeDB** Connect to GreptimeDB over the MySQL or PostgreSQL protocol and create a table, then flush it so the Iceberg metadata is published: ```sql CREATE TABLE demo ( ts TIMESTAMP(6) NOT NULL, -- TIME INDEX host STRING, cloud_region STRING, cpu DOUBLE, mem FLOAT, http_status INT, TIME INDEX (ts) ); INSERT INTO demo VALUES ('2024-01-01 00:00:00', 'h1', 'us', 12.5, 4096.0, 200), ('2024-01-01 01:00:00', 'h1', 'us', 88.8, 7000.5, 200), ('2024-01-01 02:00:00', 'h2', 'eu', 55.0, 5500.0, 503); -- Publish Iceberg metadata for the rows just written. admin flush_table('demo'); ``` **3. Query it from Spark SQL** ```sql -- The catalog is `greptime`, the namespace is the GreptimeDB schema `public`. SHOW TABLES IN greptime.public; SELECT count(*) FROM greptime.public.demo; SELECT host, round(avg(cpu), 1) AS avg_cpu FROM greptime.public.demo WHERE ts >= '2024-01-01 00:00:00' GROUP BY host ORDER BY host; ``` The table is fully queryable: time-range filters, aggregates, joins, window functions, and ordering all work as with any other Iceberg table in Spark. ## Limitations ### Read-only, single snapshot - **Read-only.** You can read GreptimeDB tables through Iceberg, but you cannot write back to GreptimeDB through the Iceberg catalog. GreptimeDB remains the sole writer. - **Only the latest snapshot (no time travel).** Compaction physically deletes old data files, so historical snapshots are not retained. The Iceberg table always reflects the current live data; you cannot query previous snapshots or roll back. - **No full schema history.** The current schema is always exposed, but past schema versions are not retained, so you cannot reconstruct what a table looked like at an earlier point in time. ### Data types and Spark GreptimeDB types map cleanly to Iceberg for the common cases (booleans, signed integers, floats, strings, binary, date, timestamp). A few things to be aware of, especially in Spark: - **Declare the time index as `TIMESTAMP(6)`.** GreptimeDB's default `TIMESTAMP` is millisecond precision, but the Iceberg schema declares the column as `timestamp` (microsecond). With a millisecond column, Spark's Parquet row-group statistics filtering compares microsecond predicates against millisecond file stats and can incorrectly drop row groups for `>`, `=`, and range queries. Declaring the time index as `TIMESTAMP(6)` makes the on-disk Parquet microsecond precision match the schema, and all comparison operators work correctly. (Second/millisecond values are still stored correctly; the issue is purely predicate pushdown against file statistics.) An existing table does not need to be recreated for this: a time index declared with second or millisecond precision can be widened in place with `ALTER TABLE`. The change is lossless — existing data is read in the new unit without rewriting, and new writes can use the finer precision: ```sql ALTER TABLE demo MODIFY COLUMN ts TIMESTAMP_US; ``` Narrowing the unit back (for example, microseconds to milliseconds) is not allowed, and widening is not supported on tables using the metric engine. See [ALTER TABLE](/reference/sql/alter.md) for details. - **Lossy type fallbacks.** `list`, `dictionary`, `json`, `interval`, `duration`, `time`, and arbitrary user `struct` types are exported as Iceberg `string` rather than a structured type, so their internal structure is not queryable through Iceberg. - **Unsigned integers are not readable in Spark.** GreptimeDB unsigned integer columns (`uint8`, `uint16`, `uint32`, `uint64`) are declared as Iceberg `long`, but the underlying Parquet files store them with unsigned physical types, which Spark cannot read. Any scan that touches an unsigned-integer column fails in Spark. If you intend to query a table through Iceberg, avoid unsigned types or store the values as signed types instead. ### Metric engine tables - **Only physical metric tables are exported.** Logical metric tables are not exposed through Iceberg; query the physical table directly. - **Sparse primary-key encoding (the metric-engine default).** All tag columns are folded into a single `__primary_key` binary column. To recover individual tag values (the logical table id, tsid, and labels) you must decode that blob according to the metric-engine sparse codec — Iceberg exposes it as opaque `binary`. The metric-internal `__table_id` and `__tsid` columns are **not** exported, so you cannot resolve a metric name to its physical table id through Iceberg. - **Prometheus native histograms** are exported as an Iceberg `struct` with list sub-fields, so each histogram value is read as a nested struct rather than a scalar. ### Operational notes - **Metadata is published asynchronously.** Freshly written rows appear in Iceberg after the next flush or compaction; flush a table manually with `admin flush_table('')` to expose them immediately. - **Old Iceberg metadata is garbage-collected** alongside the data files by GreptimeDB's normal compaction and GC — no separate maintenance is required. - **Rebuild / reconcile.** If the Iceberg export ever diverges from GreptimeDB's ground truth (a failed publish, corruption, or pre-existing tables that were never bootstrapped — see [Make a table readable](#make-a-table-readable)), an operator can rebuild a table's Iceberg metadata from scratch from the authoritative live SST set: ```bash curl -X POST \ "http://localhost:4000/v1/iceberg/v1/greptime/namespaces/public/tables//rebuild" ``` The rebuild replaces (not merges) the current snapshot. Rebuilt entries carry empty column statistics (read correctness is unaffected; only predicate pushdown / scan planning degrades until the next flush or compaction). --- ## Enterprise GreptimeDB Enterprise extends the open-source observability database with features for enterprise deployment, security, availability, operations, and workload management. Available deployment options and solutions include: - **Bring Your Own Cloud (BYOC)**: Leverage your own cloud infrastructure to host GreptimeDB, offering extensive customization and flexibility tailored to your business needs. This service includes comprehensive management of your cloud resources and robust security measures to protect your infrastructure. - **Fully Managed Dedicated Cloud**: GreptimeDB team offers a fully managed, dedicated cloud environment, ensuring peak performance, enhanced security, and exceptional reliability tailored to your enterprise needs. - **[Edge-Cloud Integrated Solution](https://greptime.com/product/carcloud)**: A comprehensive solution for managing time-series data from edge devices to the cloud, enabling real-time analytics and insights across your entire infrastructure. - Industry-specific solutions for the Internet of Things (IoT), observability, and more. ## Features GreptimeDB Enterprise supports all features available in the open-source version, which you can read about in the [User Guide](/user-guide/overview.md) documentation. To understand the differences between the open-source and enterprise versions, please visit our [Pricing Page](https://greptime.com/pricing) or [contact us](https://greptime.com/contactus). GreptimeDB Enterprise includes the following advanced features, which are described in detail in the documentation in this section: - **Database Functional Features** - Advanced Prometheus Remote Write and OpenTelemetry OTLP ingestion - [Triggers](./trigger.md): Periodically evaluate your rules and trigger external webhook. Compatible with Prometheus AlterManager. - [Automatic region load balance](./autopilot/region-balancer.md): Auto balance datanodes workload by moving regions between them. - [Scheduled Compaction](./deployments-administration/scheduled-compaction.md): Periodically submit regular compaction requests for all physical Regions, with HTTP endpoints for manual triggers and job queries. - [Read Replica](./read-replicas/overview.md): Read-only datanode instances for heavy query workloads such as analytical queries. - [Iceberg Metadata](./iceberg.md): Expose GreptimeDB tables through an Apache Iceberg REST catalog so pyiceberg, Spark, Trino, DuckDB, and other engines can read the data directly from object storage. - **Management Features** - [Greptime Enterprise Management Console](./console-ui.md): An enhanced version of our dashboard UI, carries more cluster management and monitoring features. - [Built-in User Management](./user.md): Built-in RBAC and fine-grained ACLs for data security and isolation. - [LDAP Authentication](./deployments-administration/authentication.md): Secure your system with LDAP-based authentication for access management. - [Audit Logging](./deployments-administration/monitoring/audit-logging.md): Track and monitor user activity with detailed audit logs. - [Query Guard](./deployments-administration/query-guard.md): Ban destructive operations such as `DROP TABLE`, `DELETE`, and `ALTER TABLE DROP COLUMN` for all users and reject cross-catalog access. - [Soft-Drop Tables](./soft-drop.md): Protect tables from accidental `DROP TABLE` operations and restore them from the recycle bin within the retention period. - **Advanced Deployment Modes** - [Active-Active Failover Disaster Recovery Solution](./deployments-administration/disaster-recovery/overview.md): Ensure uninterrupted service and data protection with advanced disaster recovery solution. - [Standalone Leader-Follower Mode](./deployments-administration/standalone-follower.md): Deploy standalone nodes with leader election, automatic promotion, and optional follower reads. ## Release Notes - [26.05](./release-notes/release-26_05.md) - [25.11](./release-notes/release-25_11.md) - [25.05](./release-notes/release-25_05.md) - [24.11](./release-notes/release-24_11.md) --- ## Manage Read Replicas This guide explains how to manage **Read Replicas (follower regions)** in GreptimeDB Enterprise, including how to add and remove read replicas at both the table and region levels, inspect the current regions distribution with the `SHOW REGION`, and apply placement constraints and recommended best practices for performance. :::warning Warning Read Replicas (Follower Regions) require [object storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3). Because Follower Regions may be scheduled onto different Datanodes, they must share access to Region data through object storage. Clusters using only local storage are not supported. Before allowing queries to read from Follower Regions, ensure that [GC](/user-guide/deployments-administration/manage-data/gc.md) is enabled on Metasrv and all Datanodes. Reading from Follower Regions is only supported when GC is enabled. ::: ## Adding Read Replicas to a Table Adding a Read Replica is as simple as executing one SQL command: ```sql ADMIN ADD_TABLE_FOLLOWER() ``` Read Replica peers for each region are allocated based on the workload types of the datanodes. **For optimal performance, it is strongly recommended to [configure Datanode groups](/enterprise/deployments-administration/deploy-on-kubernetes/configure-datanode-groups.md) to separate read and write workloads into different datanode groups.** This is the function in GreptimeDB for adding read replicas to. The parameters are: - `table_name`: The name of the table to add read replicas to. Next is an example to illustrate steps to add read replicas to a table. First start a GreptimeDB Enterprise Cluster with 3 Datanodes. Then create a table: ```sql CREATE TABLE foo ( ts TIMESTAMP TIME INDEX, i INT PRIMARY KEY, s STRING, ) PARTITION ON COLUMNS ('i') ( i <= 0, i > 0, ); ``` Using the `SHOW REGION`, we can find the regions distribution information: ```sql SHOW REGION FROM foo; +-------+---------------+------+--------+ | Table | Region | Peer | Leader | +-------+---------------+------+--------+ | foo | 4398046511104 | 0 | Yes | | foo | 4398046511105 | 1 | Yes | +-------+---------------+------+--------+ ``` This shows two write replicas (leader regions) on Datanodes `1` and `2`. Then to add read replicas (Follower regions): ```sql ADMIN ADD_TABLE_FOLLOWER('foo'); ``` After the read replicas are added, find the regions distribution information: ```sql SHOW REGION FROM foo; +-------+---------------+------------+--------+ | Table | Region | Peer | Leader | +-------+---------------+------------+--------+ | foo | 4398046511104 | 0 | Yes | | foo | 4398046511104 | 4294967296 | No | | foo | 4398046511105 | 1 | Yes | | foo | 4398046511105 | 4294967297 | No | +-------+---------------+------------+--------+ ``` Now, read replicas can be found on Datanode `4294967296` and `4294967297`. ## Removing Read Replicas from a Table Removing a Read Replica is as simple as executing one SQL command: ```sql ADMIN REMOVE_TABLE_FOLLOWER() ``` This is the function in GreptimeDB for removing read replicas from a table. The parameters are: - `table_name`: The name of the table to remove read replicas from. This command removes **the most recently added read replica' peers from each region**. For example, before running the command: ```sql SHOW REGION FROM foo; +-------+---------------+------------+--------+ | Table | Region | Peer | Leader | +-------+---------------+------------+--------+ | foo | 4398046511104 | 0 | Yes | | foo | 4398046511104 | 4294967296 | No | | foo | 4398046511104 | 4294967297 | No | | foo | 4398046511105 | 1 | Yes | | foo | 4398046511105 | 4294967296 | No | | foo | 4398046511105 | 4294967297 | No | +-------+---------------+------------+--------+ ``` Here, region `4398046511104` has two read replicas on peers (`4294967296`, `4294967297`), and region `4398046511105` also has two read replicas on peers (`4294967296`, `4294967297`). After executing: ```sql ADMIN REMOVE_TABLE_FOLLOWER('foo'); +------------------------------------+ | ADMIN REMOVE_TABLE_FOLLOWER('foo') | +------------------------------------+ | 0 | +------------------------------------+ ``` The most recently added read replicas' peers of each region are removed: * Region `4398046511104`: removed read replica on peer `4294967297`. * Region `4398046511105`: removed read replica on peer `4294967296`. Result: ```sql SHOW REGION FROM foo; +-------+---------------+------------+--------+ | Table | Region | Peer | Leader | +-------+---------------+------------+--------+ | foo | 4398046511104 | 0 | Yes | | foo | 4398046511104 | 4294967296 | No | | foo | 4398046511105 | 1 | Yes | | foo | 4398046511105 | 4294967297 | No | +-------+---------------+------------+--------+ ``` ## Adding Read Replica to a Region ```sql ADMIN ADD_REGION_FOLLOWER(, ) ``` This is the function in GreptimeDB for adding read replicas to a region. The parameters are: - `region_id`: The id of the region to add Read Replica to. - `datanode_id`: The id of the datanode to add Read Replica to. A read replica and a write replica cannot be placed on the same datanode. Additionally, each datanode can host only one read replica per region. Example: ```sql -- Add a read replica for region 4398046511104 on datanode 2 ADMIN ADD_REGION_FOLLOWER(4398046511104, 2); ``` If the specified datanode already hosts a read replica for that region, or is the write replica (leader) of that region, the command will be rejected. ## Removing Read Replica from a Region ```sql ADMIN REMOVE_REGION_FOLLOWER(, ) ``` This is the function in GreptimeDB for removing read replicas from a region. The parameters are: - `region_id`: The id of the region to remove read replica from. - `datanode_id`: The id of the datanode to remove read replica from. Example: ```sql -- Remove the read replica on datanode 2 for region 4398046511104 ADMIN REMOVE_REGION_FOLLOWER(4398046511104, 2); ``` ## Next steps * [Query Read Replicas](/enterprise/read-replicas/query-read-replicas.md) --- ## GreptimeDB Read Replicas Overview # Overview Read Replicas are a key feature in GreptimeDB's Enterprise Cluster Edition, designed to enhance the overall read-write performance and scalability of the database system. :::warning Warning Read Replicas (Follower Regions) require [object storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3). Because Follower Regions may be scheduled onto different Datanodes, they must share access to Region data through object storage. Clusters using only local storage are not supported. Before allowing queries to read from Follower Regions, ensure that [GC](/user-guide/deployments-administration/manage-data/gc.md) is enabled on Metasrv and all Datanodes. Reading from Follower Regions is only supported when GC is enabled. ::: In the Read Replica mechanism, clients write data to the **Leader Region (write replica)**, which then synchronizes the data to **Follower Regions (read replicas)**. Follower Regions serve as read-only replicas of the Leader Region. By [configuring Datanode groups](/enterprise/deployments-administration/deploy-on-kubernetes/configure-datanode-groups.md), Leader Regions and Follower Regions can be deployed on different Datanode nodes, read and write requests are effectively isolated, preventing resource contention and delivering a smoother experience: ## Principles GreptimeDB's Enterprise Cluster Edition leverages its architecture to enable near-zero-cost data synchronization between replicas. Additionally, Read Replicas can access newly written data with minimal latency. Below is a brief explanation of the data synchronization and read mechanisms. ### Data Synchronization In GreptimeDB, storage and compute resources are disaggregated. All data is stored in SST files on object storage. Thus, synchronizing data between Leader and Follower Regions does not require copying SST files -- only their metadata needs to be synced. Metadata is significantly smaller than SST files, making synchronization effortless. Once metadata is synced, the Read Replica "possesses" the same SST files and can access the data: ![read-replica-data-sync](/read-replica-data-sync.png) In practice, SST files metadata is persisted in a special manifest file, also stored in object storage. Each manifest file has a unique version number. Synchronizing metadata between Leader and Follower Regions essentially involves syncing this version number -- a simple integer, ensuring minimal overhead. After receiving the version number, the Follower Region fetches the manifest file from object storage, thereby obtaining the SST files metadata generated by the Leader Region. The manifest version number is synchronized via heartbeats between Regions and Metasrv. The Leader Region includes the version number in its heartbeat to Metasrv, which then forwards it to Follower Regions in their heartbeat responses: ![read-replica-heartbeat](/read-replica-heartbeat.png) It's easy to see, if there were only SST files synchronization mechanism in place, the delay for Read Replica to access written data would be the sum of the heartbeat intervals between Leader/Follower Regions and Metasrv. For example, with a default 3-second heartbeat interval, Read Replica would only see the data that are written to SST files and flushed to object store 3 to 6 seconds prior. While this suffices for clients with relaxed freshness requirements, additional mechanisms are needed for near-real-time reads. ### Data Read Newly written data are stored in the Leader Region’s memtable. To access the latest data, Follower Region needs to request the memtable data from the Leader Region. By combining this with SST files data (obtained via data sync above), the Follower Region provides clients with a complete dataset, including the most recent writes: Follower Region fetch memtable data from Leader Region via an internal gRPC interface. While this imposes some read load on the Leader Region, the impact is minimal since the memtable data resides in memory and is finite in size. ## Next steps * [Manage Read Replicas](/enterprise/read-replicas/manage-read-replicas.md) * [Query Read Replicas](/enterprise/read-replicas/query-read-replicas.md) --- ## Query Read Replicas GreptimeDB allows you to read from **Region Replicas (follower regions)** to reduce load on Write Replicas (Leader regions) and improve query scalability. You can control the read preference through both **SQL** and **HTTP** protocols. :::warning Warning Read Replicas (Follower Regions) require [object storage](/user-guide/deployments-administration/configuration.md#storage-options) (e.g., AWS S3). Because Follower Regions may be scheduled onto different Datanodes, they must share access to Region data through object storage. Clusters using only local storage are not supported. Before allowing queries to read from Follower Regions, ensure that [GC](/user-guide/deployments-administration/manage-data/gc.md) is enabled on Metasrv and all Datanodes. Reading from Follower Regions is only supported when GC is enabled. ::: ## Read Preference Options The `READ_PREFERENCE` setting accepts the following values: * **`leader`** Always read from write replicas. * **`follower`** Read only from read replicas. The query will fail if no read replicas exist. * **`follower_preferred`** Prefer read replicas. If read replicas are available, the query reads from a randomly selected read replica. If no read replica exists, the query falls back to a write replica. * **`random`** Read from a randomly selected write replica or read replica. ## SQL Protocol You can set the read preference in a SQL session: ```sql SET READ_PREFERENCE = 'follower'; ``` --- ## HTTP Protocol For HTTP requests, specify the `X-Greptime-Read-Preference` header. Example: ```bash curl -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -H "X-Greptime-Read-Preference: follower" \ -d "sql=select * from monitoring" \ http://localhost:4000/v1/sql ``` --- ## Example: Reading from Read Replicas Before reading from Read Replicas, you need to add Read Replicas to the table. See [Replica Management](/enterprise/read-replicas/manage-read-replicas.md) for more details. Insert some data into a table: ```sql INSERT INTO foo (ts, i, s) VALUES (1, -1, 's1'), (2, 0, 's2'), (3, 1, 's3'); ``` Set the read preference to read replicas: ```sql SET READ_PREFERENCE = 'follower'; ``` Query the data: ```sql SELECT * FROM foo ORDER BY ts; +----------------------------+------+------+ | ts | i | s | +----------------------------+------+------+ | 1970-01-01 00:00:00.001000 | -1 | s1 | | 1970-01-01 00:00:00.002000 | 0 | s2 | | 1970-01-01 00:00:00.003000 | 1 | s3 | +----------------------------+------+------+ ``` --- ## Verifying Follower Reads To confirm that queries are served by read replicas, use `EXPLAIN ANALYZE`: ```sql EXPLAIN ANALYZE SELECT * FROM foo ORDER BY ts; ``` * A **non-zero `other_ranges`** value indicates read replicas were involved. * With the `VERBOSE` option, you can see details like this: ```plaintext extension_ranges: [LeaderMemtableRange{leader: Peer { id: 1, addr: "192.168.50.189:14101" }, num_rows: 2, time_range: (1::Millisecond, 2::Millisecond) ... ``` If the query runs only on leaders, this `extension_ranges` section will not appear. --- ## Release 24.11 We are pleased to introduce the 24.11 release of GreptimeDB Enterprise. ## Feature Highlights ### Region Rebalance To enhance the overall resilience of GreptimeDB, region rebalancing enables flexible relocation of regions among data nodes, whether initiated manually or dynamically. This proactive approach facilitates several key benefits, including redistributing workload across nodes and efficiently migrating regions to ensure uninterrupted operation during planned maintenance activities. ### GreptimeDB Enterprise Management Console Introducing the Console UI for Managing GreptimeDB Enterprise This initial release provides a comprehensive set of features, including: * In-depth slow query analysis and troubleshooting * Detailed cluster topology information * Real-time cluster metrics and log viewer ### LDAP User Provider Bridging your own LDAP user database and authentication to GreptimeDB Enterprise. We implemented flexible configuration options for LDAP connections, supporting both simple and complex authentication mechanisms. ### Audit Logs We provided logs to track queries in the database, with information of: - Query type: read, write, DDL or others - Command: select, insert, etc. - Object type: target of the operation, for example, table, database, etc. ## Features From GreptimeDB OSS This release is based on GreptimeDB OSS v0.10. The OSS base introduces a few new features includes - Vector data type support for similarity search. - Secondary index update: user can now create secondary index on any columns. - Alter table options are added for updating table TTL, compaction parameters and full-text index settings. - JSON data type and functions support. - More geospatial UDF: spatial relation and measurement, S2 index and etc. - Initial release of Loki remote write support. See [this](https://docs.greptime.com/release-notes/release-0-10-0) for a complete changelog of 0.10 --- ## GreptimeDB Enterprise 25.05 We are pleased to introduce the 25.05 release of GreptimeDB Enterprise. ## Feature Highlights ### Elasticsearch Compatibility The compatibility layer in GreptimeDB Enterprise for Elasticsearch. This layer allows user to configure GreptimeDB Enterprise as the backend of Kibana UI, for log search, aggregation and dashboard. Queries supported in this release: - match - match_all - multi_match - term - terms - prefix - wildcard - regexp - range - exists - bool ### Read Replicas To serve analytical and other heavy queries better, we introduce dedicated query nodes in this release. These type of nodes are responsible for serve queries only, so we can push them to resource limit without affecting data ingestion. Thanks to our compute-storage disaggregated architecture, it's not a huge refactoring to add read replicas to current architecture. A new type of datanodes will play the role. It's dedicated to process query workloads. Because data are available on object storage, there will be no data replication between these types of datanodes and those original ones who take writes. User will be able to add hint to their queries to specify whether the query should run on read replicas. ### Triggers Trigger evaluate data with your predefined rules periodically and invoke webhooks if it matches. This is the initial version of triggers. Of course we designed it to work with Prometheus AlertManager. ```sql CREATE TRIGGER IF NOT EXISTS cpu_monitor ON (SELECT host AS host_label, cpu, memory FROM machine_monitor WHERE cpu > 1 and ts >= now() - '5 minutes'::INTERVAL) EVERY '5 minute'::INTERVAL LABELS (severity = 'warning') ANNOTATIONS (summary = 'CPU utilization is too high', link = 'http://...') NOTIFY( WEBHOOK alert_manager URL 'http://127.0.0.1:9093' WITH (timeout="1m") ); ``` ### Flow Reliability Reliability features are added to flow, our light-weighted streaming engine: - Task migration: move tasks between flow nodes for load balancing. ## Features From GreptimeDB OSS This release is based on GreptimeDB OSS v0.14. --- ## GreptimeDB Enterprise 25.11 We are pleased to introduce the 25.11 release of GreptimeDB Enterprise. ## Enterprise Features ### Initial release of bulk ingestion Bulk ingestion is an alternative ingestion approach introduced to overcome some limitation of our normal integration path in OSS. It's designed for high volume ingestion scenario. According to our test, an acceleration up to 5x can be achieved by bulk ingestion. In this release, we made bulk ingestion available for Prometheus remote write API. The support for other APIs are coming in our future dot releases of 25.11. There is no client API change required for adopting bulk ingestion. ### Feature completion of Triggers Trigger has seen massive improves for its feature set. Adding support for Prometheus equivalent for `for` and `keep_firing_for`, to bring in state management of trigger events. [Read more from trigger documentation](../trigger.md). ## Features From GreptimeDB OSS This release is based on GreptimeDB OSS v1.0.0. --- ## GreptimeDB Enterprise 26.05 ## 26.05.1 We are pleased to announce the **26.05.1** release of GreptimeDB Enterprise — a minor update to the 26.05 line that brings major new operational capabilities: scheduled compaction, an early preview of Apache Iceberg data-lake support, multidimensional region balancing, and finer-grained access control. This release also moves the GreptimeDB engine base to **v1.2**. _This release is based on GreptimeDB (open source engine) [v1.2](/release-notes/release-1-2-0)._ ### Feature Highlights #### Scheduled Compaction (Compaction Cronjob) Compaction can now run on a schedule managed by the meta server, instead of only on demand. The new compaction cronjob resolves compaction targets across regions, submits and tracks jobs, persists state in a KV store, and automatically cancels stale running jobs. A set of admin HTTP endpoints lets operators inspect and control scheduled compaction. This makes it straightforward to keep storage healthy on a predictable cadence with less manual intervention. #### Apache Iceberg Data Lake (Early Preview) This release introduces an **early preview** of Apache Iceberg data-lake support, beginning to cover the table lifecycle end to end: - **Repartition, truncate, and drop** for Iceberg-backed tables, with full table state lifecycle management. - **Bulk ingestion** and region-edit hooks, plus remote-compaction hooks so Iceberg data can be compacted through the enterprise compaction pipeline. - **Histogram data type** support and **metric-engine physical-table** support for Iceberg tables. Iceberg export covers tables using the default **mito engine** as well as **physical tables of the metric engine**; logical metric tables are not exposed through the catalog (query the physical table directly). #### Multidimensional Region Balancing Region balancing now reasons about more than write load. The balancer tracks per-region query CPU-rate history and evaluates read-load stability with separate eligibility rules, and supports multidimensional balancing state. The result is more stable region placement under mixed read/write workloads and during planned maintenance. #### Finer-Grained Access Control Enterprise RBAC gains several new controls for tighter, more expressive authorization: - **Named permission actions** for custom roles. - **Regex-based database ACLs**, and automatic database-ACL grants to the user who creates a database. - An optional **query guard** that can ban `DROP TABLE` / `DROP DATABASE` for all users, protecting against accidental destructive operations. #### Table Soft Drop `DROP TABLE` can be configured to **soft drop** (opt-in): the table's data is retained and recoverable with `UNDROP TABLE` rather than destroyed immediately. Soft-dropped tables keep occupying storage until they are undropped or purged. #### Operational Improvements - Configurable **query spill** for large queries under memory pressure. Query spill is not an enterprise-only capability — it has also been open-sourced in the GreptimeDB engine (see the [v1.3.0-alpha.1 release notes](/release-notes/release-1-3-0-alpha-1)). - Per-component **maximum CPU** options. - Enterprise **Flight schemas aligned by default** for consistent wire formats, and Flight **bulk inserts can now auto-create** their target tables. - **Ansible playbooks** for deploying GreptimeDB and a monitoring stack. #### From the GreptimeDB v1.2 Engine - **`JSON2`** — an improved data type for working with JSON, optimized for columnar storage. - **Manual compaction with a time range**, complementing the new scheduled compaction. - **Lifecycle event recording** — structured procedure events for database/table/view DDL, Flow DDL, repartition, GC, and WAL prune, for much better observability of cluster operations. Available from the Enterprise Dashboard UI. - **Performance**: dictionary-encoded series keys for faster queries, range-select input-projection pruning, and an asynchronous compaction picker that no longer blocks the region worker. ### Important Bug Fixes **Enterprise:** - Bulk writes now correctly validate the incoming data type for `JSONB` columns, preventing malformed data from being written. - Ingestion merge batch sizing corrected for the v1.2 engine. - Remote dynamic-filter controls handled correctly; region-balancer stability option names normalized. **From the GreptimeDB v1.2 engine:** - `PromQL` `or` queries no longer return incorrect results when operands are empty. - The MySQL protocol now **fails closed** on timestamps it cannot represent, instead of silently mishandling them. - Prometheus remote-write timeouts are now **retryable**, improving ingestion resilience under transient failures. - **Index-build reliability**: asynchronous index builds are fenced by schema generation and their publication made conditional, fixing races that could occur when index builds and schema changes ran concurrently. - **Legacy WAL options remain compatible** when upgrading, so existing configurations keep working. ### Security Updates - **Dedicated HTTP API server port** — the HTTP API is now served on a dedicated port, isolating the API surface from other server traffic. - **Sandboxed SQL local-filesystem access** — SQL execution is sandboxed so it can no longer read the local filesystem, closing a local-file-access vector. - `quinn-proto`: 0.11.14 → 0.11.16 _(dependency update)_ --- ## Soft-Drop Tables Soft-drop protects tables from accidental `DROP TABLE` operations in distributed GreptimeDB clusters. When enabled, `DROP TABLE` closes the table regions and moves the table metadata to tombstones instead of immediately deleting the physical data. You can restore the table before the retention period expires, or purge it manually when you are sure the table is no longer needed. Soft-drop applies to supported table engines in distributed mode. Standalone mode keeps the existing hard-drop behavior. File-engine tables and metric logical tables also keep hard-drop behavior when the cluster-wide soft-drop switch is enabled. ## Enable soft-drop Soft-drop is configured on Metasrv under the GC configuration. It requires Metasrv GC to be enabled. ```toml [gc] enable = true [gc.experimental_soft_drop] enable = true retention = "7d" ``` The options are: | Option | Description | | --- | --- | | `gc.enable` | Enables the Metasrv GC scheduler. Required when `gc.experimental_soft_drop.enable = true`. | | `gc.experimental_soft_drop.enable` | Enables soft-drop for supported tables in distributed mode. | | `gc.experimental_soft_drop.retention` | How long a soft-dropped table can stay in the recycle bin before Metasrv GC automatically purges it. The value must be at least `1ms`. | Restart Metasrv after changing these options. For ordinary file GC, keep Metasrv and Datanode GC settings consistent as described in [Garbage Collection](/user-guide/deployments-administration/manage-data/gc.md). ## Drop a table After soft-drop is enabled, use `DROP TABLE` as usual: ```sql DROP TABLE monitor; ``` The table becomes invisible to DDL and DML. It no longer appears in `information_schema.tables`, but it appears in [`information_schema.recycle_bin`](/reference/sql/information-schema/recycle-bin.md) until it is restored, manually purged, or automatically purged after the retention deadline. ## View dropped tables Query the recycle bin to find dropped tables and their retention deadlines: ```sql SELECT object_id, object_type, original_catalog_name, original_schema_name, original_object_name, dropped_at, retention_expires_at, purge_status, restorable FROM information_schema.recycle_bin WHERE original_schema_name = 'public'; ``` Only active, restorable soft-dropped tables are listed. Tables that are already being purged are hidden from the recycle bin. ## Restore a table Use `UNDROP TABLE` to restore a soft-dropped table: ```sql UNDROP TABLE monitor; ``` You can also use a fully qualified table name: ```sql UNDROP TABLE greptime.public.monitor; ``` The table is restored with its original data if no live table with the same full name exists. If a new live table already uses that name, `UNDROP TABLE` fails and does not restore the dropped table under another name. ## Purge a table Use the admin function `purge_table()` to permanently delete a soft-dropped table before the retention deadline: ```sql ADMIN purge_table("monitor"); ``` You can pass an unqualified, schema-qualified, or fully qualified table name: ```sql ADMIN purge_table("public.monitor"); ADMIN purge_table("greptime.public.monitor"); ``` Purging is permanent. After purge finishes, the table disappears from `information_schema.recycle_bin` and cannot be restored. `purge_table()` is available only through the `ADMIN` statement; calling it from a normal `SELECT` statement is rejected. ## Name conflict rules - You can create a new table with the same name after the old table is soft-dropped. - `UNDROP TABLE` fails if a live table with the same full name already exists. - Dropping a newly recreated table fails while an older tombstone still owns the same full name. Purge or restore the older tombstone first. - `ADMIN purge_table("table_name")` resolves the tombstoned table, not a live table with the same name. ## Automatic purge Metasrv GC periodically scans soft-dropped tables. When a table's `retention_expires_at` deadline has passed, GC submits the same purge procedure used by `ADMIN purge_table()`. The retention deadline is fixed when the table is dropped, so changing `gc.experimental_soft_drop.retention` later does not change existing dropped tables. --- ## Trigger Trigger allows you to define evaluation rules with SQL. GreptimeDB evaluates these rules periodically; once the condition is met, a notification is sent out. ## Key Features - **SQL-native**: Define trigger rules in SQL, reusing GreptimeDB's built-in functions without a learning curve - **Multi-stage state management**: Built-in pending / firing / inactive state machine prevents flapping and duplicate notifications - **Rich context**: Custom labels and annotations with automatic injection of query result fields to pinpoint root causes - **Ecosystem-friendly**: Alert payload fully compatible with Prometheus Alertmanager—use its grouping, inhibition, silencing, and routing without adapters ## Quick Start Example This section walks through an end-to-end alerting scenario: monitor system load (`load1`) and fire alerts when load exceeds a threshold. In this quick start, you will: - Create a `load1` table to store host load metrics - Define a Trigger with conditions, labels, annotations, and notifications - Simulate data ingestion with normal and abnormal values - Watch alerts transition through pending → firing → inactive ### 1. Create the Data Table Connect to GreptimeDB with a MySQL client and create the `load1` table: ```sql CREATE TABLE `load1` ( host STRING, load1 FLOAT32, ts TIMESTAMP TIME INDEX ) WITH ('append_mode'='true'); ``` ### 2. Create Trigger Connect to GreptimeDB with MySQL client and create the `load1_monitor` trigger: ```sql CREATE TRIGGER IF NOT EXISTS `load1_monitor` ON ( SELECT host AS label_host, avg(load1) AS avg_load1, max(ts) AS ts FROM public.load1 WHERE ts >= NOW() - '1 minutes'::INTERVAL GROUP BY host HAVING avg(load1) > 10 ) EVERY '1 minutes'::INTERVAL FOR '3 minutes'::INTERVAL KEEP FIRING FOR '3 minutes'::INTERVAL LABELS (severity=warning) ANNOTATIONS (comment='Your computer is smoking, should take a break.') NOTIFY( WEBHOOK alert_manager URL 'http://localhost:9093' WITH (timeout='1m') ); ``` This Trigger runs every minute, computes average load per host over the last 60 seconds, and produces an alert instance for each host where `avg(load1) > 10`. Key parameters: - **FOR**: Specifies how long the condition must continuously hold before an alert instance enters firing state. - **KEEP FIRING FOR**: Specifies how long an alert instance stays in the firing state after the condition no longer holds. See the [trigger syntax](/reference/sql/trigger-syntax.md) for more detail. ### 3. Check Trigger Status #### List all Triggers ```sql SHOW TRIGGERS; ``` Output: ```text +---------------+ | Triggers | +---------------+ | load1_monitor | +---------------+ ``` #### View the creation statement ```sql SHOW CREATE TRIGGER `load1_monitor`\G ``` Output: ```text *************************** 1. row *************************** Trigger: load1_monitor Create Trigger: CREATE TRIGGER IF NOT EXISTS `load1_monitor` ON (SELECT host AS label_host, avg(load1) AS avg_load1 ...) EVERY '1 minutes'::INTERVAL FOR '3 minutes'::INTERVAL KEEP FIRING FOR '3 minutes'::INTERVAL LABELS (severity = 'warning') ANNOTATIONS (comment = 'Your computer is smoking, should take a break.') NOTIFY( WEBHOOK `alert_manager` URL `http://localhost:9093` WITH (timeout = '1m'), ) ``` #### View Trigger details ```sql SELECT * FROM information_schema.triggers\G ``` Output: ```text *************************** 1. row *************************** trigger_name: load1_monitor trigger_id: 1024 raw_sql: (SELECT host AS label_host, avg(load1) AS avg_load1, ...) interval: 60 labels: {"severity":"warning"} annotations: {"comment":"Your computer is smoking, should take a break."} for: 180 keep_firing_for: 180 channels: [{"channel_type":{"Webhook":{"opts":{"timeout":"1m"}, ...}] flownode_id: 0 ``` See the [Triggers](/reference/sql/information-schema/triggers.md) for more details. #### View alert instances ```sql SELECT * FROM information_schema.alerts; ``` With no data written yet, this returns an empty set. See the [Alerts](/reference/sql/information-schema/alerts.md) for more details. ### 4. Write Data and Observe Alert States This script simulates data ingestion: normal values for the first minute, high values for 6 minutes to trigger alerts, then back to normal. ```bash #!/usr/bin/env bash MYSQL="mysql -h 127.0.0.1 -P 4002" insert_normal() { $MYSQL -e "INSERT INTO load1 (host, load1, ts) VALUES ('newyork1', 1.2, now()), ('newyork2', 1.1, now()), ('newyork3', 1.3, now());" } insert_high() { $MYSQL -e "INSERT INTO load1 (host, load1, ts) VALUES ('newyork1', 1.2, now()), ('newyork2', 12.1, now()), ('newyork3', 11.5, now());" } # First minute: normal data for i in {1..4}; do insert_normal; sleep 15; done # Next 6 minutes: high values for i in {1..24}; do insert_high; sleep 15; done # After: back to normal while true; do insert_normal; sleep 15; done ``` #### State Transitions In another terminal, query alert status: **Phase 1: No alerts** ```sql SELECT * FROM information_schema.alerts\G ``` Output: ``` Empty set ``` **Phase 2: pending** (condition met, `FOR` duration not reached) ```sql SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts; ``` ```text +------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+ | trigger_id | labels | active_at | fired_at | resolved_at | +------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+ | 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | NULL | NULL | | 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | NULL | NULL | +------------+-----------------------------------------------------------------------+----------------------------+----------+-------------+ ``` **Phase 3: firing** (`FOR` satisfied, notifications sent) ```sql SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts; ``` ```text +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+ | trigger_id | labels | active_at | fired_at | resolved_at | +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+ | 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | NULL | | 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | NULL | +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+-------------+ ``` **Phase 4: inactive** (condition cleared + `KEEP FIRING FOR` expired) ```sql SELECT trigger_id, labels, active_at, fired_at, resolved_at FROM information_schema.alerts; ``` ```text +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+ | trigger_id | labels | active_at | fired_at | resolved_at | +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+ | 1024 | {"alert_name":"load1_monitor","host":"newyork3","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | 2025-12-29 12:05:20.991750 | | 1024 | {"alert_name":"load1_monitor","host":"newyork2","severity":"warning"} | 2025-12-29 11:58:20.992670 | 2025-12-29 12:02:20.991713 | 2025-12-29 12:05:20.991750 | +------------+-----------------------------------------------------------------------+----------------------------+----------------------------+----------------------------+ ``` ### 5. Alertmanager Integration (Optional) If you have Prometheus Alertmanager deployed, GreptimeDB automatically pushes firing and inactive alerts to it. After each evaluation, the Trigger injects fields from the query results into labels and annotations. In this example, `host` is included as a label and `avg_load1` is included as an annotation. These fields are propagated to Alertmanager and can be referenced in notification templates. Since the payload is Alertmanager-compatible, you can use grouping, inhibition, silencing, and routing without adapters. ## Reference - [Trigger Syntax](/reference/sql/trigger-syntax.md): The syntax for SQL statements related to `TRIGGER` - [INFORMATION_SCHEMA.TRIGGERS](/reference/sql/information-schema/triggers.md): View for trigger metadata - [INFORMATION_SCHEMA.ALERTS](/reference/sql/information-schema/alerts.md): View for alert instance metadata --- ## Built-in User Management GreptimeDB Enterprise provides a built-in user and permission system backed by the Metasrv. It supports Role-Based Access Control (RBAC) and fine-grained Access Control Lists (ACLs) to ensure data security and isolation. ## Key Features - **Built-in User Management**: User accounts and permissions are stored in the Metasrv, ensuring consistent management across the cluster. - **Role-Based Access Control (RBAC)**: Assign global privileges to users, controlling operations like `SELECT`, `INSERT`, `CREATE TABLE`, and more. - **Fine-grained ACLs**: Control table-level access within specific databases using exact matches or regular expressions. - **Dynamic Management**: Manage users dynamically via HTTP APIs without restarting the server. - **Initial Seeding**: Support for seeding initial accounts from a password file at startup. ## Configuration and Explanation This section walks through how to enable the enterprise user provider and perform basic user management. ### 1. Enable User Provider To use the enterprise user and permission system, enable the `greptime_ee_user_provider` in the component that receives client requests. Configure it on the standalone server in standalone mode, or on each frontend node in cluster mode. The user provider value is: ```text greptime_ee_user_provider: ``` The password file is optional and is used only for initial account seeding. To enable the provider without seeding users, use `greptime_ee_user_provider:`. The trailing colon `:` is required by the configuration parser. **Standalone command line:** ```shell ./greptime standalone start \ --user-provider=greptime_ee_user_provider:/path/to/passwords.txt ``` **Standalone configuration file:** ```toml user_provider = "greptime_ee_user_provider:/path/to/passwords.txt" ``` ```shell ./greptime standalone start \ -c /path/to/standalone.toml ``` **Frontend command line:** ```shell ./greptime frontend start \ --metasrv-addrs=127.0.0.1:3002 \ --user-provider=greptime_ee_user_provider:/path/to/passwords.txt ``` **Frontend configuration file:** ```toml user_provider = "greptime_ee_user_provider:/path/to/passwords.txt" [meta_client] metasrv_addrs = ["127.0.0.1:3002"] ``` Then start frontend with the configuration file: ```shell ./greptime frontend start \ -c /path/to/frontend.toml ``` ### 2. Initial Account Seeding (Optional) The password file uses the following format: `[:]=` Available roles: - `admin`: Full privileges, including user management. - `readonly` (or `ro`): Read-only access (`SqlSelect`). - `writeonly` (or `wo`): Write-only access (e.g., `SqlInsert`, `TableCreate`). - `readwrite` (or `rw`): Both read and write access (default). Example `passwords.txt`: ```text # username[:role]=password superuser:admin=strong_password alice:ro=alice_pwd bob:rw=bob_pwd ``` Seeded users are granted full access (`AclType::All`) to the `public` database by default. Seed accounts are created only once. If a seeded user already exists on later startups, it is skipped. You can modify seeded users later through the UI. ## Privileges and ACLs ### Global Privileges Privileges include the following operations: | Privilege | Description | | :--- | :--- | | `TableCreate` | Create new tables | | `TableAlter` | Alter existing tables | | `TableDrop` | Drop tables | | `SqlSelect` | Execute `SELECT` queries | | `SqlInsert` | Execute `INSERT` operations | | `SqlDelete` | Execute `DELETE` operations | | `FlowCreate` | Create flows | | `FlowDrop` | Drop flows | | `DatabaseCreate` | Create databases | | `DatabaseAlter` | Alter databases | | `DatabaseDrop` | Drop databases | | `Admin` | Full administrative privileges | | `TriggerCreate` | Create triggers | | `TriggerDrop` | Drop triggers | | `TriggerAlter` | Alter triggers | #### Predefined Role Privileges When using the password file for seeding, the predefined roles map to the following privilege combinations: | Role | Privileges | | :--- | :--- | | `admin` | All privileges | | `readonly` / `ro` | `SqlSelect` | | `writeonly` / `wo` | `SqlInsert`, `SqlDelete`, `TableCreate`, `TableAlter`, `TableDrop`, `FlowCreate`, `FlowDrop`, `TriggerCreate`, `TriggerDrop`, `TriggerAlter`, `DatabaseCreate`, `DatabaseAlter`, `DatabaseDrop` | | `readwrite` / `rw` | `readonly` + `writeonly` privileges | ### Access Control Lists (ACLs) ACLs provide table-level security within a database. Each ACL entry is scoped to a database and controls which tables in that database the user can access. The `all` ACL grants access to every table in the database. Use it when a user should be able to read or write all current and future tables in that database, subject to the user's global privileges. The `match` ACL grants access to one table by exact name. Use it when a user should only access a specific table and should not automatically gain access to other tables with similar names. The `regex` ACL grants access to tables whose names match a regular expression. Use it when tables follow a naming convention and should be managed as a group. For example, `mem_.*` matches table names that start with `mem_`, `.*_metrics` matches table names that end with `_metrics`, and `sensor_[0-9]+` matches names such as `sensor_1` and `sensor_2024`. Regex ACLs are evaluated against table names within the configured database, so use specific patterns when possible to avoid granting access to more tables than intended. ## Validation Rules ### Username Usernames must: - Start with a letter (`a-z` or `A-Z`) - Contain only letters, digits, and underscores - Match the pattern `[a-zA-Z][a-zA-Z0-9_]*` ### Password Password validation depends on how the user is created or updated: - Seeded account passwords must not be empty. - Passwords created or updated through the UI must be 6 to 64 characters long. ## User Management in the Enterprise Dashboard After you enable `greptime_ee_user_provider`, both GreptimeDB and the Enterprise Dashboard require users to log in with an account. The following screenshot shows the Enterprise Dashboard login page: You can log in with the automatically created admin account or with an account defined in the seeding file. Only accounts with the `Admin` privilege can see the database management menu. Non-admin accounts can only access the query page, similar to the open-source dashboard. After logging in as an admin user, click `User Management` in the lower-left corner to open the user management page: This page lists all current users. From here, you can: 1. Create users 2. Update existing users 3. Delete users The following screenshot shows the form for creating a user: In this form, you can configure: 1. The username 2. The password 3. Whether the account has the `Admin` privilege. Non-admin users are granted the `readwrite` privilege. 4. The account ACL list The ACL form has two tabs. You can select an exact table, select an entire database to grant full-database access, or use a regular expression to grant access to a range of tables. The following screenshot shows the regular expression form: ## Reference - **Admin Account**: On the system's first startup, GreptimeDB Enterprise automatically creates a default `admin` account if it doesn't already exist. - If the environment variable `GREPTIME_ENTERPRISE_ADMIN_PASSWORD` is set, it uses that value as the password. - If the environment variable is not set, it generates a random UUID as the password. - **Checking Auto-generated Password**: If a random password was generated, you can find it in the GreptimeDB log files. Search for a message like: ```text Created admin user with auto-generated password ``` - **Resetting the Admin Password**: You can reset the `admin` password using the CLI without manually editing the KV store. The command reads backend storage settings from a Metasrv config file and supports the standard `GREPTIMEDB_METASRV__...` environment variable overrides. ```shell ./greptime-ee cli user admin-password \ --new-password \ --config-file /path/to/metasrv.toml ``` This command must be invoked from a Metasrv instance. The `--config-file` should point to the same Metasrv config file used by your Metasrv (or standalone) deployment, so the CLI can locate the correct backend storage (etcd, MySQL, or PostgreSQL). - **Persistence**: User information is persisted in the Metasrv's KV store, making it available across all frontend nodes in a cluster. - **Admin Protection**: The built-in `admin` user cannot be deleted via the API. --- ## GreptimeDB Table Engines ## Overview GreptimeDB provides three table engines with different storage models: Mito, Metric, and File. ### Mito Engine Mito is the default table engine for `CREATE TABLE` and is optimized for time-series workloads. It uses an [LSM-tree][1] design with a write-ahead log (WAL), memtables, immutable SST files, and time-window compaction. This design supports high-throughput writes while maintaining query performance. Mito can store SST files on local storage, S3, GCS, or Azure Blob Storage without additional storage plugins. When SST files are stored remotely, Mito can use a tiered local cache to reduce object-storage access latency and cost. [1]: https://en.wikipedia.org/wiki/Log-structured_merge-tree ### Metric Engine The Metric Engine is optimized for metrics workloads that create many small logical tables with similar columns, including monitoring deployments with thousands of Prometheus metric tables. Multiple logical tables share wide physical tables, which lets the engine reuse columns and metadata instead of creating separate physical storage for every logical table. This reduces storage and metadata overhead, improves columnar compression, and improves query efficiency for metrics workloads. The physical tables are stored by Mito. Applications continue to create and query logical tables independently. Sharing a physical table is an internal storage optimization; it does not merge the logical schemas or query interfaces of those tables. ### File Engine The File Engine backs tables created with `CREATE EXTERNAL TABLE`. It reads CSV, JSON, Parquet, and ORC files from local or object storage without importing or converting the data. File Engine tables are read-only and use GreptimeDB's query engine when processing external files. They can participate in SQL queries alongside regular GreptimeDB tables, which is useful for exploring or correlating existing file data before deciding whether to import it. For a complete example of querying nested JSON fields, see [Query External Data](/user-guide/query-data/query-external-data.md#query-nested-json-fields). ## Engine Selection Guide ### When to Use Each Engine - **Mito Engine**: Use Mito for most time-series workloads that need durable storage and a balance of write throughput, query performance, and storage efficiency. It is the default when `CREATE TABLE` omits the `ENGINE` clause. - **Metric Engine**: Use Metric when a metrics workload contains many logical tables with similar columns. Sharing physical storage reduces storage overhead and improves compression and query performance. - **File Engine**: Use File to explore or query supported external files without importing them into a regular table. It is suitable for one-time analysis and for files produced by existing data pipelines. ### Specifying Engine Type in SQL For a regular table, use the `ENGINE` clause of [`CREATE TABLE`](/reference/sql/create.md#create-table) to select Mito or Metric. [`CREATE EXTERNAL TABLE`](/reference/sql/create.md#create-external-table) uses the File Engine and takes the file location and format in its `WITH` clause. --- ## About GreptimeDB Version Number GreptimeDB follows [Semantic Versioning](https://semver.org/) and uses version numbers in the form `MAJOR.MINOR.PATCH`. For example, in `1.2.3`: - `1` is the major version. - `2` is the minor version. - `3` is the patch version. ## Major release(1) The major version changes when a release contains incompatible API or behavior changes. Upgrading to a new major version may require changes to clients, configuration, or deployment procedures, so review the release's upgrade notes before migrating. ## Minor release(2) The minor version changes when a release adds backward-compatible functionality. A minor release can introduce SQL features, APIs, configuration options, or operational capabilities while keeping existing public interfaces compatible. ## Revision number(3) The patch version changes when a release contains backward-compatible bug fixes, security fixes, or performance improvements. Patch releases do not intentionally introduce incompatible changes to public interfaces. --- ## Datanode The `greptime datanode` command provides subcommands for managing and benchmarking datanode instances. ## start Start the datanode service. ### Options Print the options supported by the current binary: ``` greptime datanode start --help ``` | Option | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c`/`--config-file ` | The configuration file for datanode | | `--data-home ` | Database storage root directory | | `--env-prefix ` | The prefix of environment variables, default is `GREPTIMEDB_DATANODE` | | `--http-addr ` | HTTP server address | | `--http-timeout ` | HTTP request timeout in seconds | | `--log-dir ` | Log directory | | `--log-level ` | Log level | | `--metasrv-addrs ...` | Metasrv address list | | `--node-id ` | The datanode ID | | `--grpc-bind-addr ` | The address to bind the gRPC server | | `--grpc-server-addr ` | The address advertised to the metasrv, and used for connections from outside the host. If left empty or unset, the server will automatically use the IP address of the first network interface on the host, with the same port number as the one specified in `grpc_bind_addr` | | `--wal-dir ` | Overrides the WAL directory when using the Raft Engine WAL backend | All the `addr` options are in the form of `ip:port`. ### Examples #### Start service with configurations Start a Datanode instance from a configuration file: ```sh greptime datanode start -c config/datanode.example.toml ``` Start a Datanode instance and specify its gRPC address, Metasrv address, and node ID on the command line: ```sh greptime datanode start --grpc-bind-addr=0.0.0.0:4001 --metasrv-addrs=0.0.0.0:3002 --node-id=1 ``` The [`datanode.example.toml`](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.0/config/datanode.example.toml) file is in the GreptimeDB repository. The `-c` option selects the configuration file; see [Configuration](/user-guide/deployments-administration/configuration.md) for details. ## objbench The `objbench` subcommand is a benchmarking tool for measuring read/write performance of specific files on object storage. This is useful for diagnosing performance issues and testing storage layer performance. ### Options | Option | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------------ | | `--config ` | Path to the datanode configuration file (TOML format) | | `--source ` | Source SST file path in object storage (e.g., `data/greptime/public/1024/1024_0000000000/metadata/.parquet`) | | `-v`/`--verbose` | Enable verbose output | | `--pprof-file ` | Output file path for pprof flamegraph (enables profiling). Generates an SVG flamegraph file | ### Examples #### Basic benchmark Measure the read/write performance of a specific file: ```sh greptime datanode objbench --config ./datanode.toml --source data/greptime/public/1024/1024_0000000000/metadata/8fb41bc7-a106-4b9e-879b-392da799f958.parquet ``` #### Benchmark with profiling Measure performance and generate a flamegraph for performance analysis: ```sh greptime datanode objbench --config ./datanode.toml --source data/greptime/public/1024/1024_0000000000/metadata/8fb41bc7-a106-4b9e-879b-392da799f958.parquet --pprof-file=./flamegraph.svg ``` This generates an SVG flamegraph that can be opened in a web browser for performance analysis. ## scanbench The `scanbench` subcommand benchmarks region scans directly from storage. ### Options | Option | Description | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | `--config ` | Path to the datanode/standalone configuration file (TOML format). | | `--region-id ` | Region ID in one of: `` (for example, `4398046511104`) or `:` (for example, `1024:0`). | | `--table-dir ` | Table directory used in open request (for example, `greptime/public/1024`). | | `--scanner ` | Scan strategy. Defaults to `seq`. | | `--scan-config ` | JSON file used to tune the scan request. | | `--parallelism ` | Simulated scan parallelism. Defaults to `1`. | | `--iterations ` | Benchmark iterations. Defaults to `1`. | | `--path-type ` | Region path type. Defaults to `bare`. | | `--enable-wal` | Enable WAL replay when opening the region. Disabled by default. | | `--pprof-file ` | Output file path for pprof flamegraph (Unix only). | | `--pprof-after-warmup` | Start pprof after the first iteration (use the first iteration as warmup). Requires `--pprof-file`. Disabled by default. | | `-v`/`--verbose` | Enable verbose output. | ### `scan-config` JSON ```json { "projection_names": ["host", "cpu"], "filters": ["host = 'web-1'", "cpu > 80"], "series_row_selector": "last_row" } ``` Notes: - All fields are optional. - Use either `projection` (indexes) or `projection_names` (column names), not both. - `projection_names` uses exact (case-sensitive) column name matching. - `filters` should be SQL expressions (not full SQL statements). - `series_row_selector` currently supports only `last_row`. ### Examples #### Default sequential scan ```sh greptime datanode scanbench --config ./datanode.toml --region-id 1024:0 --table-dir greptime/public/1024 ``` #### Unordered scan with parallelism ```sh greptime datanode scanbench --config ./datanode.toml --region-id 1024:0 --table-dir greptime/public/1024 --scanner unordered --parallelism 8 --iterations 5 ``` #### Series scan on metric engine data directory ```sh greptime datanode scanbench --config ./datanode.toml --region-id 1024:0 --table-dir data/greptime/public/1024 --parallelism 16 --scan-config ./scanconfig.json --scanner series --path-type data --iterations 10 ``` Example `scanconfig.json`: ```json { "projection_names": ["greptime_timestamp", "greptime_value", "az", "hostname", "region", "__tsid"], "filters": [ "mode = 'idle'", "region = 'us-west-2'", "greptime_timestamp >= 1742550540001", "greptime_timestamp <= 1742552400000", "__table_id = 1182" ] } ``` #### Profile after warmup iteration ```sh greptime datanode scanbench --config ./datanode.toml --region-id 1024:0 --table-dir greptime/public/1024 --iterations 5 --pprof-file ./scanbench.svg --pprof-after-warmup ``` --- ## Flownode ## Subcommand options Print the options supported by the current binary: ``` greptime flownode start --help ``` | Option | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c`/`--config-file ` | The configuration file for Flownode | | `--env-prefix ` | The prefix of environment variables, default is `GREPTIMEDB_FLOWNODE` | | `--http-addr ` | HTTP server address | | `--http-timeout ` | HTTP request timeout in seconds | | `--log-dir ` | Log directory | | `--log-level ` | Log level | | `--metasrv-addrs ...` | Metasrv address list | | `--node-id ` | Flownode's id | | `--grpc-bind-addr ` | The address to bind the gRPC server | | `--grpc-server-addr ` | The address advertised to the metasrv, and used for connections from outside the host. If left empty or unset, the server will automatically use the IP address of the first network interface on the host, with the same port number as the one specified in `grpc_bind_addr` | :::note When deploying a separate flownode in a cluster with frontend authentication enabled, configure the frontend internal gRPC endpoint. You can use the frontend `internal_grpc` options or the `--internal-grpc-bind-addr` and `--internal-grpc-server-addr` command line options. Flownode connects to frontends through addresses discovered from metasrv and does not send authentication headers, so it should access the frontend internal gRPC service instead of the public authenticated gRPC service. ::: ## Examples ### Start service with configurations Start a Flownode instance from a configuration file: ```sh greptime flownode start -c config/flownode.example.toml ``` Start a Flownode instance and specify its node ID, gRPC address, and Metasrv address on the command line: ```sh greptime flownode start --node-id=0 --grpc-bind-addr=127.0.0.1:6800 --metasrv-addrs=127.0.0.1:3002 ``` The [`flownode.example.toml`](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.0/config/flownode.example.toml) file is in the GreptimeDB repository. The `-c` option selects the configuration file; see [Configuration](/user-guide/deployments-administration/configuration.md) for details. --- ## Frontend ## Subcommand options Print the options supported by the current binary: ``` greptime frontend start --help ``` | Option | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c`/`--config-file ` | The configuration file for frontend | | `--disable-dashboard ` | Whether to disable the dashboard HTTP service. Defaults to `false` | | `--env-prefix ` | The prefix of environment variables, default is `GREPTIMEDB_FRONTEND` | | `--grpc-bind-addr ` | The address to bind the gRPC server | | `--grpc-server-addr ` | Address advertised to Metasrv for connections from outside the host. When unset, GreptimeDB uses the first network interface address and the port from `grpc_bind_addr` | | `--internal-grpc-bind-addr ` | The address to bind the internal gRPC server | | `--internal-grpc-server-addr ` | Address advertised to Metasrv for external connections to the internal gRPC server. When unset, GreptimeDB uses the first network interface address and the port from `internal_grpc_bind_addr` | | `--http-addr ` | HTTP server address | | `--http-timeout ` | HTTP request timeout in seconds | | `-i`/`--influxdb-enable ` | Whether to enable the InfluxDB protocol in the HTTP API | | `--log-dir ` | Log directory | | `--log-level ` | Log level | | `--metasrv-addrs ...` | Metasrv address list | | `--mysql-addr ` | MySQL server address | | `--postgres-addr ` | Postgres server address | | `--tls-cert-path ` | The TLS public key file path | | `--tls-key-path ` | The TLS private key file path | | `--tls-mode ` | TLS mode | | `--tls-watch` | Watch the TLS certificate files and reload them when they change | | `--user-provider ` | Authentication provider configuration. See [Authentication](/user-guide/deployments-administration/authentication/overview.md) | ## Examples ### Start service with configurations Start a Frontend instance from a configuration file: ```sh greptime frontend start -c config/frontend.example.toml ``` Start a Frontend instance and specify the Metasrv address on the command line: ```sh greptime frontend start --metasrv-addrs=0.0.0.0:3002 ``` The [`frontend.example.toml`](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.0/config/frontend.example.toml) file is in the GreptimeDB repository. The `-c` option selects the configuration file; see [Configuration](/user-guide/deployments-administration/configuration.md). --- ## Metasrv ## Subcommand options Print the options supported by the current binary: ``` greptime metasrv start --help ``` | Option | Description | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c`/`--config-file ` | The configuration file for metasrv | | `--enable-region-failover ` | Whether to enable region failover. Defaults to `false`. See [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md) for its prerequisites. | | `--backend ` | Metadata store backend: `etcd-store`, `memory-store`, `postgres-store`, or `mysql-store` | | `--data-home ` | Working directory of this metasrv instance | | `--env-prefix ` | The prefix of environment variables, default is `GREPTIMEDB_METASRV` | | `--grpc-bind-addr ` | The address to bind the gRPC server | | `--grpc-server-addr ` | Address advertised to Frontend, Datanode, and Flownode. When unset, GreptimeDB uses the first network interface address and the port from `grpc_bind_addr` | | `--http-addr ` | HTTP server address | | `--http-timeout ` | HTTP request timeout in seconds | | `--log-dir ` | Log directory | | `--log-level ` | Log level | | `--max-txn-ops ` | Maximum number of operations in one transaction | | `-s`/`--selector ` | Selector used for Region placement. See [Selector types](/contributor-guide/metasrv/selector.md#selector-type) | | `--store-addrs ...` | Metadata store addresses | | `--store-key-prefix ` | Key prefix used in the metadata store | ## Examples ### Start service with configurations Start Metasrv from a configuration file: ```sh greptime metasrv start -c config/metasrv.example.toml ``` The [`metasrv.example.toml`](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.0/config/metasrv.example.toml) file is in the GreptimeDB repository. The `-c` option selects the configuration file; see [Configuration](/user-guide/deployments-administration/configuration.md). --- ## GreptimeDB Command Line Overview # Overview The `greptime` binary starts GreptimeDB services and provides administrative utilities. Service subcommands accept configuration files and command-line options. ## Install the Greptime CLI The Greptime CLI is bundled with the GreptimeDB binary. If you start GreptimeDB using the binary file as described in the [installing GreptimeDB](/getting-started/installation/overview.md) documentation, you can execute the `./greptime` command from within the GreptimeDB directory. For convenience, if you prefer to run commands using `greptime` instead of `./greptime`, consider moving the CLI binary to your system's `bin` directory or adding the binary's path to your `PATH` environment variable. If you deployed GreptimeDB in Kubernetes, you can access the greptime command line through the frontend pod. Use the following command to enter the pod: ```sh kubectl exec -it -n -- /bin/bash ``` Once inside the pod, you can run `greptime help` to see all available commands. ## CLI Options The `help` command lists all available commands and options of `greptime`. ```sh $ greptime help Usage: greptime [OPTIONS] Commands: datanode Start datanode service flownode Start flownode service frontend Start frontend service metasrv Start metasrv service standalone Run greptimedb as a standalone service cli Execute the cli tools for greptimedb help Print this message or the help of the given subcommand(s) Options: --log-dir --log-level -h, --help Print help -V, --version Print version ``` ### Global options | Option | Description | | ------------------------- | ---------------------------------------------------------- | | `-h`/`--help` | Print help information | | `-V`/`--version` | Print version information | | `--log-dir ` | The logging directory, default is `./greptimedb_data/logs` | | `--log-level ` | The logging level, default is `info` | ### Subcommands - [Metasrv](/reference/command-lines/metasrv.md) - [Datanode](/reference/command-lines/datanode.md) - [Flownode](/reference/command-lines/flownode.md) - [Frontend](/reference/command-lines/frontend.md) - [Standalone](/reference/command-lines/standalone.md) ### Upgrade GreptimeDB version Please refer to [the upgrade steps](/user-guide/deployments-administration/upgrade.md) --- ## Standalone ## Subcommand options Print the options supported by the current binary: ``` greptime standalone start --help ``` | Option | Description | | --------------------------------- | ----------------------------------------------------------------------- | | `-c`/`--config-file ` | The configuration file for the standalone instance | | `--data-home ` | Working directory of the standalone instance | | `--env-prefix ` | The prefix of environment variables, default is `GREPTIMEDB_STANDALONE` | | `--http-addr ` | HTTP server address | | `-i`/`--influxdb-enable` | Whether to enable InfluxDB protocol in HTTP API | | `--log-dir ` | Log directory | | `--log-level ` | Log level | | `--mysql-addr ` | MySQL server address | | `--postgres-addr ` | Postgres server address | | `--grpc-bind-addr ` | The address to bind the gRPC server | | `--tls-mode ` | TLS mode | | `--tls-cert-path ` | TLS certificate file | | `--tls-key-path ` | TLS private key file | | `--tls-watch` | Watch TLS certificate files and reload them when they change | | `--user-provider ` | Authentication provider configuration. See [Authentication](/user-guide/deployments-administration/authentication/overview.md) | ## Examples ### Start standalone with configurations Start GreptimeDB in standalone mode from a configuration file: ```sh greptime --log-dir=greptimedb_data/logs --log-level=info standalone start -c config/standalone.example.toml ``` The [`standalone.example.toml`](https://github.com/GreptimeTeam/greptimedb/blob/v1.2.0/config/standalone.example.toml) file is in the GreptimeDB repository. The `-c` option selects the configuration file; see [Configuration](/user-guide/deployments-administration/configuration.md). --- ## Data Export & Import(Utilities) The Export and Import tools provide functionality for backing up and restoring GreptimeDB databases. These tools can handle both schema and data, allowing for complete or selective backup and restoration operations. ## Export Tool ### Command Syntax ```bash greptime cli data export [OPTIONS] ``` ### Options | Option | Required | Default | Description | | ------------------------- | -------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--addr` | Yes | - | Server address to connect | | `--output-dir` | Yes, unless remote storage is configured | - | Directory used for export output. Required when no remote object store is configured. **Important:** for data exports, this path is resolved on the GreptimeDB server; schema SQL files are written by the CLI process. When the CLI and server run on different hosts, use remote object storage and set `--ddl-local-dir` for local SQL files. | | `--database` | No | all databases | Name of the database to export | | `--db-parallelism`, `-j` | No | 1 | Number of databases to export in parallel. For example, if there are 20 databases and `db-parallelism` is set to 4, then 4 databases will be exported concurrently. (alias: `--export-jobs`) | | `--table-parallelism` | No | 4 | Number of tables to export in parallel within a single database. For example, if a database contains 30 tables and `table-parallelism` is set to 8, then 8 tables will be exported concurrently. | | `--max-retry` | No | 3 | Maximum retry attempts per job | | `--target`, `-t` | No | all | Export target (schema/data/all) | | `--start-time` | No | - | Start of time range for data export | | `--end-time` | No | - | End of time range for data export | | `--auth-basic` | No | - | Use the `:` format | | `--timeout` | No | 0 | The timeout for a single call to the DB, default is 0 which means never timeout (e.g., `30s`, `10min 20s`) | | `--proxy ` | No | - | The proxy server address to connect, if set, will override the system proxy. The default behavior will use the system proxy if neither `proxy` nor `no_proxy` is set. | | `--no-proxy` | No | - | Disable proxy server, if set, will not use any proxy | | `--s3` | No | - | If export data to s3 | | `--ddl-local-dir` | No | - | If both `ddl_local_dir` and remote storage (S3/OSS/GCS/Azure Blob) are set, `ddl_local_dir` will be only used for exported SQL files, and the data will be exported to remote storage. Note that `ddl_local_dir` export sql files to **LOCAL** file system, this is useful if export client don't have direct access to remote storage. If remote storage is set but `ddl_local_dir` is not set, both SQL&data will be exported to remote storage. | | `--s3-bucket` | Yes* | - | The s3 bucket name if s3 is set, this is required | | `--s3-root` | Yes* | - | If s3 is set, this is required | | `--s3-endpoint` | No* | - | The s3 endpoint if s3 is set, this is required | | `--s3-access-key-id` | Yes* | - | The s3 access key ID if s3 is set, this is required | | `--s3-secret-access-key` | Yes* | - | The s3 secret access key if s3 is set, this is required | | `--s3-region` | Yes* | - | The s3 region if s3 is set, this is required | | `--oss` | No | - | If export data to oss | | `--oss-bucket` | Yes* | - | The oss bucket name if oss is set, this is required | | `--oss-endpoint` | No* | - | The oss endpoint if oss is set, this is required | | `--oss-access-key-id` | Yes* | - | The oss access key id if oss is set, this is required | | `--oss-access-key-secret` | Yes* | - | The oss access key secret if oss is set, this is required | | `--gcs` | No | - | If export data to Google Cloud Storage (GCS) | | `--gcs-bucket` | Yes* | - | The GCS bucket name if gcs is set, this is required | | `--gcs-root` | Yes* | - | If gcs is set, this is required | | `--gcs-scope` | No | - | GCS service scope | | `--gcs-credential` | No | - | GCS credential content | | `--gcs-endpoint` | No | - | GCS endpoint URL | | `--azblob` | No | - | If export data to Azure Blob Storage | | `--azblob-container` | Yes* | - | The Azure Blob container name if azblob is set, this is required | | `--azblob-root` | Yes* | - | If azblob is set, this is required | | `--azblob-account-name` | Yes* | - | The Azure Blob account name if azblob is set, this is required | | `--azblob-account-key` | No | - | Azure Blob account key | | `--azblob-endpoint` | No | - | Azure Blob endpoint URL | | `--azblob-sas-token` | No | - | Azure Blob SAS token | ### Export Targets - `schema`: Exports table schemas only (`SHOW CREATE TABLE`) - `data`: Exports table data only (`COPY DATABASE TO`) - `all`: Exports both schemas and data (default) ## Import Tool ### Command Syntax ```bash greptime cli data import [OPTIONS] ``` ### Options | Option | Required | Default | Description | | ------------------------ | -------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--addr` | Yes | - | Server address to connect | | `--input-dir` | Yes | - | Directory containing backup data | | `--database` | No | all databases | Name of the database to import | | `--db-parallelism`, `-j` | No | 1 | Number of databases to import in parallel. For example, if there are 20 databases and `db-parallelism` is set to 4, then 4 databases will be imported concurrently. (alias: `--import-jobs`) | | `--max-retry` | No | 3 | Maximum retry attempts per job | | `--target`, `-t` | No | all | Import target (schema/data/all) | | `--auth-basic` | No | - | Use the `:` format | ### Import Targets - `schema`: Imports table schemas only - `data`: Imports table data only - `all`: Imports both schemas and data (default) --- ## Metadata Interaction The `greptime cli meta` command can be used to interact with the metadata of GreptimeDB cluster. ## Common options | Option | Description | Default | Values | | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------- | | `--store-addrs ...` | The endpoint of store. One of etcd, postgres or mysql. For postgres store, the format is: `"password=password dbname=postgres user=postgres host=localhost port=5432"`. For etcd store, the format is: `"127.0.0.1:2379"`. For mysql store, the format is: `"mysql://user:password@ip:port/dbname"` | - | - | | `--max-txn-ops ` | The maximum number of operations in a transaction. Only used when using [etcd-store] | 128 | - | | `--backend ` | The metadata store backend | etcd-store | etcd-store, memory-store, postgres-store, mysql-store | | `--store-key-prefix ` | The key prefix of the metadata store | - | - | | `--meta-table-name ` | The table name in RDS to store metadata. Only used when using [postgres-store] or [mysql-store] | greptime_metakv | - | | `--meta-schema-name ` | PostgreSQL schema of the metadata table. Must match the Metasrv `meta_schema_name` setting. If not set, the `search_path` of the connection is used. Only used when backend is `postgres-store` | - | - | | `--backend-tls-mode ` | TLS mode for the metadata store connection (etcd, PostgreSQL, or MySQL). Set it to match the TLS requirement of the metadata store | disable | disable, prefer, require, verify-ca, verify-full | | `--backend-tls-cert-path ` | Path to the client certificate file for the metadata store connection | "" | - | | `--backend-tls-key-path ` | Path to the client private key file for the metadata store connection | "" | - | | `--backend-tls-ca-cert-path ` | Path to the CA certificate file for the metadata store connection | "" | - | ## Get key-value pair ### Command syntax ```bash greptime cli meta get key [OPTIONS] [KEY] ``` ### Options | Option | Description | Default | | ----------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | | `--prefix` | Whether to perform a prefix query. If true, returns all key-value pairs where the key starts with the given prefix | false | | `--limit ` | The maximum number of key-value pairs to return. If 0, returns all key-value pairs | 0 | ## Get table metadata ### Command syntax ```bash greptime cli meta get table [OPTIONS] ``` ### Options | Option | Description | Default | | ------------------------------- | -------------------------------- | -------- | | `--table-id ` | Get table metadata by table id | - | | `--table-name ` | Get table metadata by table name | - | | `--schema-name ` | The schema name of the table | public | | `--catalog-name ` | The catalog name of the table | greptime | | `--pretty` | Pretty print the output | - | ## Put key-value pair ### Command syntax ```bash greptime cli meta put key [OPTIONS] [KEY] ``` ### Options | Option | Description | Default | | --------------- | ---------------------------------------- | ------- | | `--value-stdin` | Read the value from standard input | - | | `--no-validate` | Skip metadata validation before writing | false | :::note `--value-stdin` is required. ::: ## Put table metadata ### Put table info #### Command syntax ```bash greptime cli meta put table info [OPTIONS] ``` #### Options | Option | Description | Default | | ------------------------------- | ---------------------------------------------------------------------- | -------- | | `--table-id ` | Select table by table id | - | | `--table-name ` | Select table by table name | - | | `--schema-name ` | The schema name of the table | public | | `--catalog-name ` | The catalog name of the table | greptime | | `--value-stdin` | Read the JSON-encoded `TableInfoValue` from standard input (required) | - | ### Put table route #### Command syntax ```bash greptime cli meta put table route [OPTIONS] ``` #### Options | Option | Description | Default | | ------------------------------- | ----------------------------------------------------------------------- | -------- | | `--table-id ` | Select table by table id | - | | `--table-name ` | Select table by table name | - | | `--schema-name ` | The schema name of the table | public | | `--catalog-name ` | The catalog name of the table | greptime | | `--value-stdin` | Read the JSON-encoded `TableRouteValue` from standard input (required) | - | ## Delete key-value pair ### Command syntax ```bash greptime cli meta del key [OPTIONS] [KEY] ``` ### Options | Option | Description | Default | | ---------- | --------------------------------------------- | ------- | | `--prefix` | Delete key-value pairs with the given prefix. | false | ## Delete table metadata ### Command syntax ```bash greptime cli meta del table [OPTIONS] ``` #### Options | Option | Description | Default | | ------------------------------- | -------------------------------- | -------- | | `--table-id ` | Get table metadata by table id | - | | `--table-name ` | Get table metadata by table name | - | | `--schema-name ` | The schema name of the table | public | | `--catalog-name ` | The catalog name of the table | greptime | ## Examples ### Get single key-value pair ```bash greptime cli meta get key --store-addrs=$ENDPOINT \ --backend=postgres-store \ __table_name/greptime/public/metric_table_2 ``` Output: ```json __table_name/greptime/public/metric_table_2 {"table_id":1059} ``` ### Get all key-value pairs with the prefix Output: ```bash greptime cli meta get key --prefix \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ __table_name/greptime/public ``` ```json __table_name/greptime/public/greptime_physical_table {"table_id":1057} __table_name/greptime/public/metric_table_1 {"table_id":1058} __table_name/greptime/public/metric_table_2 {"table_id":1059} ``` ### Get table metadata by table id ```bash greptime cli meta get table --table-id=1059 \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ --pretty ``` Output: ```json __table_info/1059 { "table_info": { "ident": { "table_id": 1059, "version": 0 }, "name": "metric_table_2", "desc": null, "catalog_name": "greptime", "schema_name": "public", "meta": { "schema": { "column_schemas": [ { "name": "app", "data_type": { "String": null }, "is_nullable": true, "is_time_index": false, "default_constraint": null, "metadata": {} }, ... ], "timestamp_index": 2, "version": 0 }, "primary_key_indices": [ 0, ... ], "value_indices": [ 3 ], "engine": "metric", "next_column_id": 8, "region_numbers": [ 0, ... ], "options": { "write_buffer_size": null, "ttl": null, "skip_wal": false, "extra_options": { "on_physical_table": "greptime_physical_table" } }, "created_on": "2025-06-17T14:53:14.639207075Z", "partition_key_indices": [] }, "table_type": "Base" }, "version": 0 } __table_route/1059 { "type": "logical", "physical_table_id": 1057, "region_ids": [ 4548370366464, 4548370366465, ... ] } ``` ### Get table metadata by table name ```bash greptime cli meta get table --table-name=metric_table_2 \ --schema-name=public \ --store-addrs=$ENDPOINT \ --backend=postgres-store ``` Output: same as the output of the command above. ### Put table info ```bash cat table_info.json | greptime cli meta put table info \ --table-id=1059 \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ --value-stdin ``` Output: ```bash Table(1059) info updated ``` ### Put table route ```bash cat table_route.json | greptime cli meta put table route \ --table-id=1059 \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ --value-stdin ``` Output: ```bash Table(1059) route updated ``` ## Delete non-existent key-value pair ```bash greptime cli meta del key --store-addrs=$ENDPOINT \ --backend=postgres-store \ non_existent_key ``` Output(Return deleted key-value pairs count): ```bash 0 ``` ## Delete a key-value pair ```bash greptime cli meta del key --store-addrs=$ENDPOINT \ --backend=postgres-store \ __table_name/greptime/public/metric_table_3 ``` Output(Return deleted key-value pairs count): ```bash 1 ``` ## Delete a table metadata ```bash greptime cli meta del table --table-id=1059 \ --store-addrs=$ENDPOINT \ --backend=postgres-store ``` Output: ```bash Table(1059) deleted ``` --- ## Metadata Export & Import(Utilities) The snapshot commands save, restore, and inspect GreptimeDB metadata snapshots. ## Export Tool ### Command Syntax ```bash greptime cli meta snapshot save [OPTIONS] ``` ### Options #### Storage Backend Options | Option | Required | Default | Description | | ------------------ | -------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | --store-addrs | Yes, for persistent backends | - | Metadata store addresses. Required for persistent backends; the format depends on the backend | | --backend | No | etcd-store | Metadata store backend: `etcd-store`, `memory-store`, `postgres-store`, `mysql-store`, or `raft-engine-store` | | --store-key-prefix | No | "" | Unified prefix for data in metasrv, refer to metasrv configuration | | --meta-table-name | No | greptime_metakv | When backend is one of `postgres-store`, `mysql-store`, the table name storing metadata | | --meta-schema-name | No | - | PostgreSQL schema of the metadata table. Must match the Metasrv `meta_schema_name` setting. If not set, the `search_path` of the connection is used. Only used when backend is `postgres-store` | | --backend-tls-mode | No | disable | TLS mode for the metadata store connection (etcd, PostgreSQL, or MySQL). Set it to match the TLS requirement of the metadata store: `disable`, `prefer`, `require`, `verify-ca`, or `verify-full` | | --backend-tls-cert-path | No | "" | Path to the client certificate file for the metadata store connection | | --backend-tls-key-path | No | "" | Path to the client private key file for the metadata store connection | | --backend-tls-ca-cert-path | No | "" | Path to the CA certificate file for the metadata store connection | | --max-txn-ops | No | 128 | Maximum number of txn operations | #### File Options | Option | Required | Default | Description | | ------------ | -------- | ----------------- | --------------------------------------------------------------------------- | | --file-path | No | metadata_snapshot.metadata.fb | Path of the snapshot file | | --dir | No | / | Root directory used for file or object-store I/O | #### Object Storage Options To use object storage for storing exported metadata, enable one of the following providers and configure its connection parameters: ##### S3 | Option | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------------------------- | | --s3 | No | false | Whether to use S3 as storage medium for exported data | | --s3-bucket | No | - | S3 bucket name | | --s3-root | No | - | Root path in S3 bucket | | --s3-access-key-id | No | - | S3 access key ID | | --s3-secret-access-key | No | - | S3 secret access key | | --s3-region | No | - | S3 region name | | --s3-endpoint | No | - | S3 endpoint URL (optional, defaults based on bucket region) | | --s3-enable-virtual-host-style | No | false | Enable virtual host style for S3 API requests | ##### OSS (Alibaba Cloud) | Option | Required | Default | Description | | ----------------------- | -------- | ------- | -------------------------------------- | | --oss | No | false | Whether to use OSS for exported data | | --oss-bucket | No | - | OSS bucket name | | --oss-root | No | - | Root path in OSS bucket | | --oss-access-key-id | No | - | OSS access key ID | | --oss-access-key-secret | No | - | OSS access key secret | | --oss-endpoint | No | - | OSS endpoint URL | ##### GCS (Google Cloud Storage) | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------- | | --gcs | No | false | Whether to use GCS for exported data | | --gcs-bucket | No | - | GCS bucket name | | --gcs-root | No | - | Root path in GCS bucket | | --gcs-scope | No | - | GCS service scope | | --gcs-credential | No | - | GCS credential content | | --gcs-endpoint | No | - | GCS endpoint URL | ##### Azure Blob Storage | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------------- | | --azblob | No | false | Whether to use Azure Blob for exported data | | --azblob-container | No | - | Azure Blob container name | | --azblob-root | No | - | Root path in container | | --azblob-account-name | No | - | Azure Blob account name | | --azblob-account-key | No | - | Azure Blob account key | | --azblob-endpoint | No | - | Azure Blob endpoint URL | | --azblob-sas-token | No | - | Azure Blob SAS token | ## Import Tool ### Command Syntax ```bash greptime cli meta snapshot restore [OPTIONS] ``` ### Options #### Storage Backend Options | Option | Required | Default | Description | | ------------------ | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | --store-addrs | Yes, for persistent backends | - | Metadata store addresses. Required for persistent backends; the format depends on the backend | | --backend | No | etcd-store | Metadata store backend: `etcd-store`, `memory-store`, `postgres-store`, `mysql-store`, or `raft-engine-store` | | --store-key-prefix | No | "" | Unified prefix for data in metasrv, refer to metasrv configuration | | --meta-table-name | No | greptime_metakv | When backend is `postgres-store`, `mysql-store`, the table name storing metadata | | --meta-schema-name | No | - | PostgreSQL schema of the metadata table. Must match the Metasrv `meta_schema_name` setting. If not set, the `search_path` of the connection is used. Only used when backend is `postgres-store` | | --backend-tls-mode | No | disable | TLS mode for the metadata store connection (etcd, PostgreSQL, or MySQL). Set it to match the TLS requirement of the metadata store: `disable`, `prefer`, `require`, `verify-ca`, or `verify-full` | | --backend-tls-cert-path | No | "" | Path to the client certificate file for the metadata store connection | | --backend-tls-key-path | No | "" | Path to the client private key file for the metadata store connection | | --backend-tls-ca-cert-path | No | "" | Path to the CA certificate file for the metadata store connection | | --max-txn-ops | No | 128 | Maximum number of txn operations | #### File Options | Option | Required | Default | Description | | ----------- | -------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | --file-path | No | metadata_snapshot.metadata.fb | Path of the snapshot file to restore | | --dir | No | / | Root directory used for file or object-store I/O | | --force | No | false | Whether to force import, when target backend is detected to not be in a clean state, import is disabled by default, enable this flag to force import | #### Object Storage Options To use object storage for importing metadata, enable one of the following providers and configure its connection parameters: ##### S3 | Option | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------------------------- | | --s3 | No | false | Whether to use S3 as storage medium for exported data | | --s3-bucket | No | - | S3 bucket name | | --s3-root | No | - | Root path in S3 bucket | | --s3-access-key-id | No | - | S3 access key ID | | --s3-secret-access-key | No | - | S3 secret access key | | --s3-region | No | - | S3 region name | | --s3-endpoint | No | - | S3 endpoint URL (optional, defaults based on bucket region) | | --s3-enable-virtual-host-style | No | false | Enable virtual host style for S3 API requests | ##### OSS (Alibaba Cloud) | Option | Required | Default | Description | | ----------------------- | -------- | ------- | -------------------------------------- | | --oss | No | false | Whether to use OSS for exported data | | --oss-bucket | No | - | OSS bucket name | | --oss-root | No | - | Root path in OSS bucket | | --oss-access-key-id | No | - | OSS access key ID | | --oss-access-key-secret | No | - | OSS access key secret | | --oss-endpoint | No | - | OSS endpoint URL | ##### GCS (Google Cloud Storage) | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------- | | --gcs | No | false | Whether to use GCS for exported data | | --gcs-bucket | No | - | GCS bucket name | | --gcs-root | No | - | Root path in GCS bucket | | --gcs-scope | No | - | GCS service scope | | --gcs-credential | No | - | GCS credential content | | --gcs-endpoint | No | - | GCS endpoint URL | ##### Azure Blob Storage | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------------- | | --azblob | No | false | Whether to use Azure Blob for exported data | | --azblob-container | No | - | Azure Blob container name | | --azblob-root | No | - | Root path in container | | --azblob-account-name | No | - | Azure Blob account name | | --azblob-account-key | No | - | Azure Blob account key | | --azblob-endpoint | No | - | Azure Blob endpoint URL | | --azblob-sas-token | No | - | Azure Blob SAS token | ## Info Tool The Info tool allows you to view the contents of a metadata snapshot without restoring it. ### Command Syntax ```bash greptime cli meta snapshot info [OPTIONS] ``` ### Options #### File Options | Option | Required | Default | Description | | ------------ | -------- | ----------------- | ------------------------------------------- | | --file-path | No | metadata_snapshot.metadata.fb | Path of the snapshot file to inspect | | --dir | No | / | Root directory used for file or object-store I/O | | --inspect-key| No | "*" | Query pattern to filter metadata keys | | --limit | No | - | Maximum number of entries to display | #### Object Storage Options To inspect snapshots stored in object storage, enable one of the following providers and configure its connection parameters: ##### S3 | Option | Required | Default | Description | | ---------------------------- | -------- | ------- | ---------------------------------------------------------------- | | --s3 | No | false | Whether to use S3 as storage medium for the snapshot | | --s3-bucket | No | - | S3 bucket name | | --s3-root | No | - | Root path in S3 bucket | | --s3-access-key-id | No | - | S3 access key ID | | --s3-secret-access-key | No | - | S3 secret access key | | --s3-region | No | - | S3 region name | | --s3-endpoint | No | - | S3 endpoint URL (optional, defaults based on bucket region) | | --s3-enable-virtual-host-style | No | false | Enable virtual host style for S3 API requests | ##### OSS (Alibaba Cloud) | Option | Required | Default | Description | | ----------------------- | -------- | ------- | -------------------------------------- | | --oss | No | false | Whether to use OSS for the snapshot | | --oss-bucket | No | - | OSS bucket name | | --oss-root | No | - | Root path in OSS bucket | | --oss-access-key-id | No | - | OSS access key ID | | --oss-access-key-secret | No | - | OSS access key secret | | --oss-endpoint | No | - | OSS endpoint URL | ##### GCS (Google Cloud Storage) | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------- | | --gcs | No | false | Whether to use GCS for the snapshot | | --gcs-bucket | No | - | GCS bucket name | | --gcs-root | No | - | Root path in GCS bucket | | --gcs-scope | No | - | GCS service scope | | --gcs-credential | No | - | GCS credential content | | --gcs-endpoint | No | - | GCS endpoint URL | ##### Azure Blob Storage | Option | Required | Default | Description | | --------------------- | -------- | ------- | ------------------------------------------- | | --azblob | No | false | Whether to use Azure Blob for the snapshot | | --azblob-container | No | - | Azure Blob container name | | --azblob-root | No | - | Root path in container | | --azblob-account-name | No | - | Azure Blob account name | | --azblob-account-key | No | - | Azure Blob account key | | --azblob-endpoint | No | - | Azure Blob endpoint URL | | --azblob-sas-token | No | - | Azure Blob SAS token | --- ## Repair logical tables The `greptime cli meta repair logical-tables` command can be used to repair logical tables for GreptimeDB cluster. In some cases, the logical tables metadata may be inconsistent with metadata stored in the metadata store. This command can be used to repair the logical tables metadata. :::tip The tool needs to connect to both the metadata store and datanode. Ensure that the cluster is running and the tool can communicate with the datanode. ::: ## Command syntax ```bash greptime cli meta repair logical-tables [OPTIONS] ``` ## Options | Option | Description | Default | Values | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------- | | `--store-addrs ...` | The endpoint of store. One of etcd, postgres or mysql. For postgres store, the format is: `"password=password dbname=postgres user=postgres host=localhost port=5432"`. For etcd store, the format is: `"127.0.0.1:2379"`. For mysql store, the format is: `"mysql://user:password@ip:port/dbname"` | - | - | | `--max-txn-ops ` | The maximum number of operations in a transaction. Only used when using [etcd-store] | 128 | - | | `--backend ` | The metadata store backend | etcd-store | etcd-store, memory-store, postgres-store, mysql-store | | `--store-key-prefix ` | The key prefix of the metadata store | - | - | | `--meta-table-name ` | The table name in RDS to store metadata. Only used when using [postgres-store] or [mysql-store] | greptime_metakv | - | | `--meta-schema-name ` | PostgreSQL schema of the metadata table. Must match the Metasrv `meta_schema_name` setting. If not set, the `search_path` of the connection is used. Only used when backend is `postgres-store` | - | - | | `--backend-tls-mode ` | TLS mode for the metadata store connection (etcd, PostgreSQL, or MySQL). Set it to match the TLS requirement of the metadata store | disable | disable, prefer, require, verify-ca, verify-full | | `--backend-tls-cert-path ` | Path to the client certificate file for the metadata store connection | "" | - | | `--backend-tls-key-path ` | Path to the client private key file for the metadata store connection | "" | - | | `--backend-tls-ca-cert-path ` | Path to the CA certificate file for the metadata store connection | "" | - | | `--table-names ` | The names of the tables to repair, separated by comma | - | | `--table-ids ` | The id of the table to repair, separated by comma | - | | `--schema-name ` | The schema of the tables to repair | public | | `--catalog-name ` | The catalog of the tables to repair | greptime | | `--fail-fast` | Whether to fail fast if any repair operation fails | - | | `--client-timeout-secs ` | The timeout for the client to operate the datanode | 30 | | `--client-connect-timeout-secs ` | The timeout for the client to connect to the datanode | 3 | ## Examples ### Repair logical tables by table names ```bash greptime cli meta repair logical-tables --store-addrs=$ENDPOINT \ --backend=postgres-store \ --table-names=metric_table_1,metric_table_2 \ --schema-name=public \ --catalog-name=greptime ``` Output: ```bash 2025-06-20T08:31:43.904497Z INFO cli::metadata::repair: All alter table requests sent successfully for table: greptime.public.metric_table_1 2025-06-20T08:31:43.904499Z INFO cli::metadata::repair: All alter table requests sent successfully for table: greptime.public.metric_table_2 2025-06-20T08:31:43.904539Z INFO cli::metadata::repair: Repair logical tables result: 2 tables repaired, 0 tables skipped ``` --- ## Repair partition columns The `greptime cli meta repair partition-column` command can be used to repair partition columns for GreptimeDB cluster. ## When to use this tool Before the [PR-6494](https://github.com/GreptimeTeam/greptimedb/pull/6494), the columns for table partitioning could refer to invalid ones in the table metadata. For example, when a new column is added into the table before the partition columns. If you find that reads or writes fail because of incorrect partition columns, you can use this tool. This tool will scan all the tables' metadata and set the partition columns to the right ones. ## Command syntax ```bash greptime cli meta repair partition-column [OPTIONS] ``` ## Options | Option | Description | Default | Value | | - | - | - | - | | `--store-addrs ` | The endpoint of store. One of etcd, postgres or mysql. For postgres store, the format is: `"password=password dbname=postgres user=postgres host=localhost port=5432"`. For etcd store, the format is: `"127.0.0.1:2379"`. For mysql store, the format is: `"mysql://user:password@ip:port/dbname"` | | string | | `--backend ` | The metadata store backend | `etcd-store` | one of:`etcd-store``memory-store``postgres-store``mysql-store` | | `--max-txn-ops ` | The maximum number of operations in a transaction. Only used when using the `etcd-store`. | `128` | number | | `--store-key-prefix ` | The key prefix of the metadata store | "" | string | | `--meta-table-name ` | The table name in RDS to store metadata. Only used when using `postgres-store` or `mysql-store` | `greptime_metakv` | string | | `--meta-schema-name ` | PostgreSQL schema of the metadata table. Must match the Metasrv `meta_schema_name` setting. If not set, the `search_path` of the connection is used. Only used when backend is `postgres-store`. | | string | | `--backend-tls-mode ` | TLS mode for the metadata store connection (etcd, PostgreSQL, or MySQL). Set it to match the TLS requirement of the metadata store. | `disable` | one of:`disable``prefer``require``verify-ca``verify-full` | | `--backend-tls-cert-path ` | Path to the client certificate file for the metadata store connection. | "" | string | | `--backend-tls-key-path ` | Path to the client private key file for the metadata store connection. | "" | string | | `--backend-tls-ca-cert-path ` | Path to the CA certificate file for the metadata store connection. | "" | string | | `--dry-run` | Report invalid partition columns without changing table metadata. Use this flag to inspect the proposed changes before running the repair. | `false` | flag | | `--update-limit ` | The maximum times this tool does the alterations to the table metadata. This option can be used to gradually update the table metadata | unlimited | number | ## Example ```bash greptime cli meta repair partition-column \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ --dry-run \ --update-limit 1 ``` --- ## GreptimeDB Glossary This glossary defines GreptimeDB terms for observability data, storage, querying, and operations. --- ## A ### Anomaly Detection The process of identifying data points, events, or observations that deviate significantly from the norm. In time-series data, anomaly detection helps in spotting unusual patterns that may indicate critical incidents. ### Append Only Table A table type that keeps every written row and does not support row updates or deletion. It skips deduplication and is commonly used for immutable logs and events. --- ## C ### Cardinality A measure of the uniqueness of data elements in a database, such as the number of unique values in a column. High cardinality can increase the complexity and storage requirements of a database, especially in time-series data. ### Cloud-Native Design An architectural approach that uses cloud infrastructure and services for deployment, scaling, and recovery. GreptimeDB can run as a standalone process at the edge or as a distributed cluster. ### Columnar Storage A data layout that stores values by column rather than by row. Queries can read only the columns they need, and values with similar types and distributions can be compressed together. --- ## D ### Datanode A core component in GreptimeDB's distributed architecture responsible for data storage and processing. Datanodes handle data ingestion, storage management, query execution on local data, and maintain regions containing actual table data. Multiple datanodes can be deployed across a cluster to provide horizontal scalability, fault tolerance, and distributed data processing capabilities. ### Decoupled Compute and Storage Architecture An architecture in which compute and persistent data storage are managed separately. In distributed GreptimeDB deployments backed by shared object storage, Datanodes can be added or removed without moving every persistent data file between them. --- ## E ### Edge Database A database deployed at the edge of a network, close to the data source or user, to minimize latency and optimize data processing in real-time. ### Edge Deployment The practice of running a system close to its data source to reduce network latency and bandwidth use. GreptimeDB standalone can run on edge devices that meet its resource requirements. ### Event Management The practice of collecting, organizing, and analyzing events such as logs, alerts, and state changes. --- ## F ### Field A column role for measurements, log content, trace attributes, and other values. Field columns do not participate in the primary key. ### Frontend The query processing layer in GreptimeDB's distributed architecture that serves as the entry point for client connections. Frontend nodes handle SQL parsing, query planning, distributed query coordination, and result aggregation. They route queries to appropriate datanodes, manage client sessions, and provide protocol compatibility for various database interfaces including MySQL, PostgreSQL, and GreptimeDB's native protocols. ### Flow Engine GreptimeDB's engine for continuous computation over incoming rows from source tables. It materializes results in sink tables. Aggregation and TQL workloads use batching mode; the original streaming mode is deprecated and is not recommended for new workloads. --- ## G ### GreptimeCloud GreptimeDB's fully managed database service. It provides hosted GreptimeDB instances with managed deployment, scaling, upgrades, and monitoring. --- ## I ### IoT Cloud A cloud computing platform specifically designed to support Internet of Things (IoT) applications by providing the necessary storage, processing power, and connectivity to manage IoT data at scale. ### IoT Database A database optimized for high-frequency data from Internet of Things (IoT) devices, including time-series measurements, device events, and logs. It provides scalable storage and querying as the number of devices and data volume grow. ### IoT Observability The use of metrics, logs, and events to monitor the state and behavior of IoT devices and the services that operate them, helping teams maintain reliability and diagnose performance problems. ### Interoperability The ability of systems to exchange data through compatible interfaces. GreptimeDB supports SQL interfaces and selected APIs from InfluxDB, OpenTelemetry, Prometheus, Elasticsearch, and Loki. Each compatibility layer has its own documented scope. --- ## J ### JSON2 [`JSON2`](/user-guide/logs/json2.md) is a Beta column type for logs and other semi-structured data. It stores JSON subpaths in a structured columnar form and supports dot-path access, `json_get`, and optional type hints. JSON2 currently requires an append-only table. --- ## L ### Log Aggregation Perform calculations on a set of logs to generate a single summary statistic for analysis and troubleshooting. For example, SUM, COUNT, etc. ### Logical and Physical Tables A logical table is the table users create and query. A physical table is the internal storage table that holds its data. Mito Engine normally maps a logical table to its own physical storage, while Metric Engine can map many logical metric tables to shared physical tables. Sharing a physical table does not merge their logical schemas or query interfaces. ### Log Management The process of collecting, storing, querying, and visualizing log data to support performance analysis and security investigations. ### LSM-Tree (Log-Structured Merge Tree) A data structure used by GreptimeDB's storage engine that optimizes write performance by initially writing data to a log and periodically merging these logs into sorted structures. This design is particularly effective for time-series workloads with high write throughput. --- ## M ### Memory Leak A type of software bug where a program fails to release unused memory, causing a gradual decrease in available memory and potential system instability over time. ### Metasrv The metadata management service in GreptimeDB's distributed architecture that maintains cluster state, table schemas, and region distribution information. Metasrv coordinates cluster operations, manages table creation and modifications, handles region assignments and migrations, and ensures metadata consistency across the cluster. It acts as the central control plane for cluster management and serves as the source of truth for all metadata operations. ### Metric Engine GreptimeDB's storage engine for metrics workloads with many logical tables. It maps logical tables to shared physical wide tables so that they can reuse columns and metadata, reducing storage overhead and improving columnar compression and query efficiency. Metric Engine uses Mito Engine for physical storage. ### Mito Engine The default storage engine in GreptimeDB. Mito uses an LSM-tree design with WAL, memtables, immutable SST files, and compaction. It supports local and object-storage providers and can use local cache for remote data. --- ## O ### Observability A measure of how well a system's internal state can be inferred from its outputs. Metrics, logs, traces, and events provide evidence for monitoring and debugging. ### OpenTelemetry An open-source framework with APIs, SDKs, and protocols for collecting and exporting metrics, logs, and traces. GreptimeDB accepts these signals through OTLP/HTTP. --- ## P ### Pipeline A GreptimeDB configuration that parses and transforms incoming data before storage. Processors parse or modify fields, the transform section maps fields to table columns and types, and a dispatcher can select a pipeline based on values in an incoming record. Pipelines support timestamp parsing, regular-expression matching, field extraction, and type conversion so that observability data can be stored in a structure that supports efficient queries. ### Primary Key One or more Tag columns that identify a series or record group. In a table that uses deduplication, the primary key and time index identify rows that are merged according to the table's merge mode. It is not a general relational uniqueness constraint: append-only tables can omit the primary key and can retain repeated keys and timestamps as separate rows. ### PromQL (Prometheus Query Language) The query language used by Prometheus for time-series data. GreptimeDB provides near 100% PromQL compatibility, including support for existing Prometheus dashboards and alerting rules. See the [documented limitations and compatibility list](/user-guide/query-data/promql.md#limitations). --- ## R ### Read Replica An Enterprise feature that adds read-only Follower Regions to a table to scale read workloads and reduce query load on Leader Regions. Leader and Follower Regions share SST files in object storage; the leader synchronizes SST metadata, and followers fetch unflushed data from the leader when serving current reads. Datanode groups can place leaders and followers on different nodes to isolate read and write workloads, improve query response time, support geographically distributed access, and improve read availability. See [Read Replicas](/enterprise/read-replicas/overview.md). ### Region A fundamental unit of data distribution in GreptimeDB's architecture. Regions contain a subset of table data and can be distributed across different nodes in a cluster. Each region manages its own storage, indexing, and query processing, enabling horizontal scalability and fault tolerance. ### Repartition The process of adjusting table partition boundaries after creation by merging existing partitions and splitting them with new rules. Repartition is used to better match current data distribution, relieve hotspots, and reduce small cold regions. ### Rust A systems programming language with static memory-safety guarantees. GreptimeDB is implemented in Rust. --- ## S ### Scalability The ability of a system to handle more data or work by adding resources to existing nodes or by adding nodes. Scaling does not remove the need to plan partitioning, cache, query concurrency, and workload distribution. ### SQL (Structured Query Language) A standardized language for defining, managing, and querying relational data. GreptimeDB supports SQL for metrics, logs, traces, and event data. ### Stream Processing The continuous, real-time processing of data streams as they arrive. GreptimeDB's Flow Engine provides continuous aggregation through batching mode; its original streaming mode is deprecated and is not recommended for new workloads. --- ## T ### Table Engine A table engine controls how table data is written, organized, compacted, and read. Mito Engine is the general-purpose engine for time-indexed tables. Metric Engine is built on Mito Engine and is optimized for large numbers of logical metric tables. ### Table Sharding The technique of splitting a large table into multiple smaller partitions. In GreptimeDB, table sharding helps distribute load across regions and improve throughput for hot or large tables. ### Tag A column role used to identify a time series. Rows with the same Tag values belong to the same series. Tags commonly store dimensions such as host names, service names, or device IDs and are declared as `PRIMARY KEY` columns. ### Time Index A special timestamp column in GreptimeDB tables that serves as the primary time dimension for time-series data. Every GreptimeDB table requires exactly one Time Index column to organize data chronologically, enable time-based queries, and support efficient time-series operations like downsampling and time-window aggregations. ### Time Series Database A specialized database designed to handle time-series data, which consists of sequences of data points indexed by timestamps. GreptimeDB supports time-series workloads as part of a broader model for metrics, logs, traces, and event data. ### Trigger An Enterprise feature that evaluates SQL rules at configured intervals and sends webhook notifications when their conditions match. Trigger supports Alertmanager-compatible labels and annotations. --- ## U ### Unified Analysis Analysis across signal types through shared query tools. GreptimeDB supports SQL across metrics, logs, traces, and event tables, and PromQL for metrics. ### Unified Observability A database architecture that handles metrics, logs, traces, and events through shared data-model concepts, storage infrastructure, and query tools. GreptimeDB applies common Tag, Timestamp, and Field semantics across these signals while allowing them to remain in separate tables. Wide events are an optional modeling approach within this foundation. --- ## V ### Vector Processing A query-execution technique that processes batches of values rather than one value at a time. GreptimeDB's query engine uses vectorized execution and can use SIMD instructions for supported operations. ### Vehicle Data Collection The process of collecting vehicle data such as sensor readings, GPS locations, and diagnostics. ### Vehicle-Cloud Integrated TSDB A time-series database deployment in which vehicle or edge systems collect data and a cloud service stores and queries the aggregated data. This model supports efficient storage and real-time analysis for connected-vehicle applications. --- ## W ### WAL (Write-Ahead Log) A logging mechanism used by GreptimeDB to ensure data durability and consistency. WAL records all data changes before they are applied to the main storage, enabling recovery in case of system failures. GreptimeDB supports flexible WAL options including local disk storage or distributed services like Kafka. ### Wide Events A structured telemetry record with many fields describing one operation or business event. A wide event can include high-cardinality values such as user IDs, session IDs, trace IDs, business attributes, and request metadata. It is commonly associated with the Observability 2.0 approach, but does not replace native metrics, logs, or traces. GreptimeDB can store wide events as timestamped data alongside those signals. --- --- ## gtctl [gtctl][1], g-t-control, is a command-line tool for managing the GreptimeDB clusters. gtctl is the **All-in-One** binary that integrates with multiple operations of GreptimeDB clusters. ## Installation ### One-line Installation Download the binary using the following command: ```bash curl -fsSL https://raw.githubusercontent.com/greptimeteam/gtctl/develop/hack/install.sh | sh ``` After downloading, the gtctl will be in the current directory. You also can install gtctl from the AWS-CN S3 bucket: ```bash curl -fsSL https://downloads.greptime.cn/releases/scripts/gtctl/install.sh | sh -s -- -s aws ``` ### Homebrew On macOS, gtctl is available via Homebrew: ```bash brew tap greptimeteam/greptime brew install gtctl ``` ### From Source If you already have the [Go][2] installed, you can run the `make` command under this project to build gtctl: ```bash make gtctl ``` After building, the gtctl will be generated in `./bin/`. If you want to install gtctl, you can run the `install` command: ```bash # The built gtctl will be installed in /usr/local/bin. make install # The built gtctl will be installed in your customed path. make install INSTALL_DIR= ``` ## Enable gtctl Autocompletion (Optional) The gtctl supports autocompletion of several different shells. ### Bash The gtctl completion script for Bash can be generated with the command `gtctl completion bash`. Sourcing the completion script in your shell enables gtctl autocompletion. ```bash echo 'source <(gtctl completion bash)' >> ~/.bashrc ``` ### Zsh The gtctl completion script for Zsh can be generated with the command `gtctl completion zsh`. Sourcing the completion script in your shell enables gtctl autocompletion. ```bash mkdir -p $ZSH/completions && gtctl completion zsh > $ZSH/completions/_gtctl ``` ### Fish The gtctl completion script for Fish can be generated with the command `gtctl completion fish`. Sourcing the completion script in your shell enables gtctl autocompletion. ```bash gtctl completion fish | source ``` ## Quick Start The **fastest** way to experience the GreptimeDB cluster is to use the playground: ```bash gtctl playground ``` The `playground` will deploy the minimal GreptimeDB cluster on your environment in **bare-metal** mode. ## Deployments The gtctl supports two deployment mode: Kubernetes and Bare-Metal. ### Kubernetes #### Prerequisites * Kubernetes 1.18 or higher version is required. You can use the [kind][3] to create your own Kubernetes cluster: ```bash kind create cluster ``` #### Create Create your own GreptimeDB cluster and etcd cluster: ```bash gtctl cluster create mycluster -n default ``` If you want to use artifacts(charts and images) that are stored in the CN region, you can enable `--use-greptime-cn-artifacts`: ```bash gtctl cluster create mycluster -n default --use-greptime-cn-artifacts ``` After creating, the whole GreptimeDB cluster will start in the default namespace: ```bash # Get the cluster. gtctl cluster get mycluster -n default # List all clusters. gtctl cluster list ``` All the values used for cluster, etcd and operator which provided in [charts][4] are configurable, you can use `--set` to configure them. The gtctl also surface some commonly used configurations, you can use `gtctl cluster create --help` to browse them. ```bash # Configure cluster datanode replicas to 5 gtctl cluster create mycluster --set cluster.datanode.replicas=5 # Two same ways to configure etcd storage size to 15Gi gtctl cluster create mycluster --set etcd.storage.volumeSize=15Gi gtctl cluster create mycluster --etcd-storage-size 15Gi ``` #### Dry Run gtctl provides `--dry-run` option in cluster creation. If a user executes the command with `--dry-run`, gtctl will output the manifests content without applying them: ```bash gtctl cluster create mycluster -n default --dry-run ``` #### Connect You can use the kubectl `port-forward` command to forward frontend requests: ```bash kubectl port-forward svc/mycluster-frontend 4002:4002 > connections.out & ``` Use gtctl `connect` command or your `mysql` client to connect to your cluster: ```bash gtctl cluster connect mycluster -p mysql mysql -h 127.0.0.1 -P 4002 ``` #### Scale (Experimental) You can use the following commands to scale (or down-scale) your cluster: ```bash # Scale datanode to 3 replicas. gtctl cluster scale -n -c datanode --replicas 3 # Scale frontend to 5 replicas. gtctl cluster scale -n -c frontend --replicas 5 ``` #### Delete If you want to delete the cluster, you can: ```bash # Delete the cluster. gtctl cluster delete mycluster -n default # Delete the cluster, including etcd cluster. gtctl cluster delete mycluster -n default --tear-down-etcd ``` ### Bare-Metal #### Create You can deploy the GreptimeDB cluster on a bare-metal environment by the following simple command: ```bash gtctl cluster create mycluster --bare-metal ``` It will create all the necessary meta information on `${HOME}/.gtctl`. If you want to do more configurations, you can use the yaml format config file: ```bash gtctl cluster create mycluster --bare-metal --config ``` You can refer to the example config `cluster.yaml` and `cluster-with-local-artifacts.yaml` provided in [`examples/bare-metal`][5]. #### Delete Since the cluster in bare-metal mode runs in the foreground, any `SIGINT` and `SIGTERM` will delete the cluster. For example, the cluster will be deleted after pressing `Ctrl+C` on keyboard. The deletion will also delete the meta information of one cluster which located in `${HOME}/.gtctl`. The logs of cluster will remain if `--retain-logs` is enabled (enabled by default). ## Development There are many useful tools provided through Makefile, you can simply run make help to get more information: * Compile the project ```bash make ``` Then the gtctl will be generated in `./bin/`. * Run the unit test ```bash make test ``` * Run the e2e test ```bash make e2e ``` [1]: [2]: [3]: [4]: [5]: --- ## HTTP API Endpoint List GreptimeDB provides two HTTP servers: | Server | Default address | Purpose | |--------|----------------|---------| | **Main HTTP server** | `127.0.0.1:4000` | Internal / operational use. Serves all paths, including admin endpoints such as `/health`, `/metrics`, `/config`, and `/debug/*`, as well as all `/v1` and `/dashboard` paths. Keep this port private and accessible only by trusted operators. | | **Public HTTP API server** | `127.0.0.1:4006` | User-facing API traffic. Serves only `/v1` APIs and `/dashboard`, excluding the admin endpoints. Disabled by default; enable it with `http.enable_api_server = true` in your configuration file. | Keep the main HTTP server private. To expose GreptimeDB APIs to end users, enable the public API server and expose only that port. :::warning The public API server reduces the exposed route surface, but route filtering does not provide authentication or transport security. With no `user_provider` configured, GreptimeDB accepts requests without authentication. Before allowing external access, configure authentication, TLS, and network access controls, either directly or through a reverse proxy. ::: ```toml [http] # Main HTTP server — keep this internal addr = "127.0.0.1:4000" # Enable the public API server and bind it for external access enable_api_server = true api_server_addr = "0.0.0.0:4006" ``` See the [configuration documentation](/user-guide/deployments-administration/configuration.md#protocol-options) for all `[http]` options. The following sections describe the commonly used HTTP paths: ## Admin APIs Unversioned endpoints outside `/v1`, used for health checks, status, metrics, and other administrative operations. :::note Admin API endpoints are available **only** on the main HTTP server (default port `4000`). They are not exposed by the dedicated public API server even when `http.enable_api_server` is enabled. ::: ### Health Check - **Path**: `/health` - **Methods**: `GET`, `POST` - **Description**: Provides a health check endpoint to verify that the server is running. - **Usage**: Access this endpoint to check the health status of the server. Please refer to the [check GreptimeDB health documentation](/user-guide/deployments-administration/monitoring/check-db-status.md#check-if-greptimedb-is-running-normally) for an example. ### Status - **Path**: `/status` - **Methods**: `GET` - **Description**: Retrieves the current status of the server. - **Usage**: Use this endpoint to obtain server status information. Please refer to the [Check GreptimeDB status documentation](/user-guide/deployments-administration/monitoring/check-db-status.md#check-greptimedb-runtime-status) for an example. ### Metrics - **Path**: `/metrics` - **Methods**: `GET` - **Description**: Exposes Prometheus metrics for monitoring purposes. - **Usage**: Prometheus can scrape this endpoint to collect metrics data. Example: ```bash curl -X GET http://127.0.0.1:4000/metrics ``` Output: ```text # HELP greptime_app_version app version # TYPE greptime_app_version gauge greptime_app_version{app="greptime-edge",short_version="main-b4bd34c5",version="0.12.0"} 1 # HELP greptime_catalog_catalog_count catalog catalog count # TYPE greptime_catalog_catalog_count gauge greptime_catalog_catalog_count 1 # HELP greptime_catalog_schema_count catalog schema count # TYPE greptime_catalog_schema_count gauge greptime_catalog_schema_count 3 # HELP greptime_flow_run_interval_ms flow run interval in ms # TYPE greptime_flow_run_interval_ms gauge greptime_flow_run_interval_ms 1000 # HELP greptime_meta_create_catalog meta create catalog # TYPE greptime_meta_create_catalog histogram greptime_meta_create_catalog_bucket{le="0.005"} 1 greptime_meta_create_catalog_bucket{le="0.01"} 1 greptime_meta_create_catalog_bucket{le="0.025"} 1 greptime_meta_create_catalog_bucket{le="0.05"} 1 greptime_meta_create_catalog_bucket{le="0.1"} 1 ... ``` ### Configuration - **Path**: `/config` - **Methods**: `GET` - **Description**: Retrieves the server's configuration options. - **Usage**: Access this endpoint to get configuration details. For example: ```shell curl http://localhost:4000/config ``` The output contains the configuration information of the GreptimeDB server. ```toml enable_telemetry = true user_provider = "static_user_provider:file:user" init_regions_in_background = false init_regions_parallelism = 16 [http] addr = "127.0.0.1:4000" timeout = "30s" body_limit = "64MiB" is_strict_mode = false # ... ``` ### Dashboard - **Paths**: `/dashboard` - **Methods**: `GET`, `POST` - **Description**: Provides access to the server's dashboard interface. - **Usage**: Access these endpoints to interact with the web-based dashboard. The dashboard is packaged with GreptimeDB when the corresponding build feature is enabled. Its source code is in the [GreptimeDB Dashboard repository](https://github.com/GreptimeTeam/dashboard). ### Log Level - **Path**: `/debug/log_level` - **Methods**: `POST` - **Description**: Adjusts the server's log level dynamically. - **Usage**: Send a log level change request to this endpoint. For more information, refer to the [how-to documentation](https://github.com/GreptimeTeam/greptimedb/blob/main/docs/how-to/how-to-change-log-level-on-the-fly.md). ### Enable/Disable Trace - **Path**: `/debug/enable_trace` - **Methods**: `POST` - **Description**: Dynamically enables or disables distributed tracing at runtime. - **Usage**: Send `true` to enable tracing or `false` to disable tracing. Example to enable tracing: ```bash curl --data "true" http://127.0.0.1:4000/debug/enable_trace # Output: trace enabled ``` Example to disable tracing: ```bash curl --data "false" http://127.0.0.1:4000/debug/enable_trace # Output: trace disabled ``` For more information on tracing configuration, refer to the [tracing documentation](/user-guide/deployments-administration/monitoring/tracing.md). ### Profiling Tools - **Base Path**: `/debug/prof/` - **Description**: Runtime profiling for CPU or memory usage on the database node. CPU profiling: | Path | Method | Description | | --- | --- | --- | | `/debug/prof/cpu` | `POST` | Collects a CPU profile. Query parameters include `seconds`, `frequency`, and `output`. Supported output formats are `proto`, `text`, and `flamegraph`. | Example: ```bash curl -X POST -s 'http://127.0.0.1:4000/debug/prof/cpu?seconds=10&output=flamegraph' > greptime-cpu.svg ``` Memory profiling: | Path | Method | Description | | --- | --- | --- | | `/debug/prof/mem` | `POST` | Dumps memory profiling data. Query parameter `output` supports `text`, `proto`, and `flamegraph`. | | `/debug/prof/mem/status` | `GET` | Checks whether heap profiling is active. | | `/debug/prof/mem/activate` | `POST` | Activates heap profiling. | | `/debug/prof/mem/deactivate` | `POST` | Deactivates heap profiling. | | `/debug/prof/mem/gdump` | `GET` | Checks whether jemalloc gdump is active. | | `/debug/prof/mem/gdump` | `POST` | Activates or deactivates jemalloc gdump. Use form field `activate=true` or `activate=false`. | | `/debug/prof/mem/symbol` | `POST` | Uploads a jemalloc heap dump file and returns a symbolicated flamegraph. | Examples: ```bash curl -X POST -s 'http://127.0.0.1:4000/debug/prof/mem?output=flamegraph' > greptime-mem.svg curl -X GET 'http://127.0.0.1:4000/debug/prof/mem/status' curl -X POST 'http://127.0.0.1:4000/debug/prof/mem/gdump' -d 'activate=true' ``` For operational guidance, see [Collect profiling data](/user-guide/deployments-administration/performance-tuning/performance-tuning-tips.md#collect-profiling-data). For advanced usage, refer to [Profiling CPU](https://github.com/GreptimeTeam/greptimedb/blob/main/docs/how-to/how-to-profile-cpu.md) and [Profiling Memory](https://github.com/GreptimeTeam/greptimedb/blob/main/docs/how-to/how-to-profile-memory.md). ## Query Endpoints These endpoints execute SQL, PromQL, or structured log queries. ### SQL API - **Path**: `/v1/sql` - **Methods**: `GET`, `POST` - **Description**: Executes SQL queries against the server. - **Usage**: Send SQL queries in the request body. For more information on the SQL API, refer to the [HTTP API documentation](/user-guide/protocols/http.md#post-sql-statements) in the user guide. ### Format SQL API - **Path**: `/v1/sql/format` - **Methods**: `GET`, `POST` - **Description**: Rewrites an SQL statement into the canonical form of GreptimeDB's SQL dialect. Available since v0.17. - **Usage**: Pass the SQL in the `sql` query parameter, or in the form body of a `POST` request, which must set `Content-Type: application/x-www-form-urlencoded`. Returns a JSON object `{"formatted": "..."}` holding the normalized SQL string. For more information on the Format SQL API, refer to the [HTTP API documentation](/user-guide/protocols/http.md#format-sql-with-greptimedbs-sql-dialect) in the user guide. ### Parse SQL API - **Path**: `/v1/sql/parse` - **Methods**: `GET`, `POST` - **Description**: Parses SQL and returns the GreptimeDB statement representation without executing it. - **Usage**: Pass SQL in the `sql` query parameter or in an `application/x-www-form-urlencoded` form body. ### PromQL API - **Path**: `/v1/promql` - **Methods**: `GET`, `POST` - **Description**: Executes PromQL queries for Prometheus-compatible metrics, and returns data in GreptimeDB's JSON format. - **Usage**: Send PromQL queries in the request body. For more information on the PromQL API, refer to the [PromQL documentation](/user-guide/query-data/promql.md). ## Protocol Endpoints These endpoints implement selected APIs from InfluxDB, Prometheus, OpenTelemetry, Loki, Splunk HEC, and OpenTSDB. ### InfluxDB Compatibility - **Paths**: - `/v1/influxdb/write` - `/v1/influxdb/api/v2/write` - `/v1/influxdb/ping` - `/v1/influxdb/health` - **Methods**: - `POST` for write endpoints. - `GET` for ping and health endpoints. - **Description**: Provides endpoints compatible with InfluxDB for data ingestion and health checks. - **Usage**: - Ingest data using InfluxDB line protocol. - Use ping and health endpoints to check server status. The detailed documentation for InfluxDB protocol can be found at [here](/user-guide/protocols/influxdb-line-protocol.md). ### Prometheus Remote Write/Read - **Paths**: - `/v1/prometheus/write` - `/v1/prometheus/read` - **Methods**: `POST` - **Description**: Supports Prometheus remote write and read APIs. - **Usage**: - Send metric data using Prometheus remote write protocol. - Read metric data using Prometheus remote read protocol. ### Prometheus HTTP API - **Base Path**: `/v1/prometheus/api/v1` - **Endpoints**: - `/format_query` - `/status/buildinfo` - `/query` - `/query_range` - `/labels` - `/metadata` - `/series` - `/parse_query` - `/label/{label_name}/values` - **Methods**: `GET`, `POST` - **Description**: Provides Prometheus HTTP API endpoints for querying and retrieving metric data. - **Usage**: Use these endpoints to interact with metrics using standard Prometheus HTTP API. Refer to the original Prometheus documentation for more information on the [Prometheus HTTP API](https://prometheus.io/docs/prometheus/latest/querying/api/). ### OpenTelemetry Protocol (OTLP) - **Paths**: - `/v1/otlp/v1/metrics` - `/v1/otlp/v1/traces` - `/v1/otlp/v1/logs` - **Methods**: `POST` - **Description**: Supports OpenTelemetry protocol for ingesting metrics, traces, and logs. - **Usage**: Send OpenTelemetry formatted data to these endpoints. ### Loki Compatibility - **Path**: `/v1/loki/api/v1/push` - **Methods**: `POST` - **Description**: Compatible with Loki's API for log ingestion. - **Usage**: Send log data in Loki's format to this endpoint. ### Splunk HEC Compatibility - **Path**: `/v1/splunk/services/collector/event`, `/v1/splunk/services/collector/raw`, `/v1/splunk/services/collector/health` - **Methods**: `POST` for the ingestion endpoints, `GET` for the health endpoint - **Description**: Compatible with the Splunk HTTP Event Collector (HEC) protocol for log ingestion. - **Usage**: Send JSON events to `/event` or plain text to `/raw`. See [Ingest Data with Splunk](/user-guide/ingest-data/for-observability/splunk.md). ### OpenTSDB Protocol - **Path**: `/v1/opentsdb/api/put` - **Methods**: `POST` - **Description**: Supports data ingestion using the OpenTSDB protocol. - **Usage**: Ingest time series data using OpenTSDB's JSON format. ## Log and Pipeline Endpoints | Path | Methods | Description | | --- | --- | --- | | `/v1/ingest` | `POST` | Ingests logs through a Pipeline. | | `/v1/logs` | `GET`, `POST` | Executes a structured log query. The request uses a JSON body; `POST` is the usual method. | | `/v1/pipelines/{pipeline_name}` | `GET` | Returns a Pipeline definition. | | `/v1/pipelines/{pipeline_name}` | `POST` | Creates or replaces a Pipeline. | | `/v1/pipelines/{pipeline_name}` | `DELETE` | Deletes a Pipeline. | | `/v1/pipelines/{pipeline_name}/ddl` | `GET` | Returns the table DDL inferred from a Pipeline. | | `/v1/pipelines/_dryrun` | `POST` | Runs a Pipeline against sample input without ingesting the result. | See [Log overview](/user-guide/logs/overview.md), [Log query](/user-guide/query-data/log-query.md), and [Pipeline configuration](/reference/pipeline/pipeline-config.md). --- ## Built-in Pipelines GreptimeDB offers built-in pipelines for common log formats, allowing you to use them directly without creating new pipelines. Note that the built-in pipelines are not editable. Additionally, the "greptime_" prefix of the pipeline name is reserved. ## `greptime_identity` The `greptime_identity` pipeline is designed for writing JSON logs and automatically creates columns for each field in the JSON log. Nested JSON objects are automatically flattened into separate columns using dot notation. - Nested objects are automatically flattened (e.g., `{"a": {"b": 1}}` becomes column `a.b`) - Arrays are converted to JSON strings - An error is returned if the same field has different types - Fields with `null` values are ignored - If time index is not specified, an additional column, `greptime_timestamp`, is added to the table as the time index to indicate when the log was written ### Type conversion rules - `string` -> `string` - `number` -> `int64` or `float64` - `boolean` -> `bool` - `null` -> ignore - `array` -> `string` (JSON-stringified) - `object` -> automatically flattened into separate columns (see [Flatten JSON objects](#flatten-json-objects)) For example, if we have the following json data: ```json [ {"name": "Alice", "age": 20, "is_student": true, "score": 90.5,"object": {"a":1,"b":2}}, {"age": 21, "is_student": false, "score": 85.5, "company": "A" ,"whatever": null}, {"name": "Charlie", "age": 22, "is_student": true, "score": 95.5,"array":[1,2,3]} ] ``` We'll merge the schema for each row of this batch to get the final schema. Note that nested objects are automatically flattened into separate columns (e.g., `object.a`, `object.b`), and arrays are converted to JSON strings. The table schema will be: ```sql mysql> desc pipeline_logs; +--------------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+---------------------+------+------+---------+---------------+ | age | Int64 | | YES | | FIELD | | is_student | Boolean | | YES | | FIELD | | name | String | | YES | | FIELD | | object.a | Int64 | | YES | | FIELD | | object.b | Int64 | | YES | | FIELD | | score | Float64 | | YES | | FIELD | | company | String | | YES | | FIELD | | array | String | | YES | | FIELD | | greptime_timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | +--------------------+---------------------+------+------+---------+---------------+ 9 rows in set (0.00 sec) ``` The data will be stored in the table as follows: ```sql mysql> select * from pipeline_logs; +------+------------+---------+----------+----------+-------+---------+-----------+----------------------------+ | age | is_student | name | object.a | object.b | score | company | array | greptime_timestamp | +------+------------+---------+----------+----------+-------+---------+-----------+----------------------------+ | 22 | 1 | Charlie | NULL | NULL | 95.5 | NULL | [1,2,3] | 2024-10-18 09:35:48.333020 | | 21 | 0 | NULL | NULL | NULL | 85.5 | A | NULL | 2024-10-18 09:35:48.333020 | | 20 | 1 | Alice | 1 | 2 | 90.5 | NULL | NULL | 2024-10-18 09:35:48.333020 | +------+------------+---------+----------+----------+-------+---------+-----------+----------------------------+ 3 rows in set (0.01 sec) ``` ### Specify time index A time index is necessary in GreptimeDB. Since the `greptime_identity` pipeline does not require a YAML configuration, you must set the time index in the query parameters if you want to use the timestamp from the log data instead of the automatically generated timestamp when the data arrives. Example of Incoming Log Data: ```JSON [ {"action": "login", "ts": 1742814853} ] ``` To instruct the server to use ts as the time index, set the following query parameter in the HTTP header: ```shell curl -X "POST" "http://localhost:4000/v1/ingest?db=public&table=pipeline_logs&pipeline_name=greptime_identity&custom_time_index=ts;epoch;s" \ -H "Content-Type: application/json" \ -H "Authorization: Basic " \ -d $'[{"action": "login", "ts": 1742814853}]' ``` The `custom_time_index` parameter accepts two formats, depending on the input data format: - Epoch number format: `;epoch;` - The field can be an integer or a string. - The resolution must be one of: `s`, `ms`, `us`, or `ns`. - Date string format: `;datestr;` - For example, if the input data contains a timestamp like `2025-03-24 19:31:37+08:00`, the corresponding format should be `%Y-%m-%d %H:%M:%S%:z`. With the configuration above, the resulting table will correctly use the specified log data field as the time index. ```sql DESC pipeline_logs; ``` ```sql +--------+-----------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------+-----------------+------+------+---------+---------------+ | ts | TimestampSecond | PRI | NO | | TIMESTAMP | | action | String | | YES | | FIELD | +--------+-----------------+------+------+---------+---------------+ 2 rows in set (0.02 sec) ``` Here are some example of using `custom_time_index` assuming the time variable is named `input_ts`: - 1742814853: `custom_time_index=input_ts;epoch;s` - 1752749137000: `custom_time_index=input_ts;epoch;ms` - "2025-07-17T10:00:00+0800": `custom_time_index=input_ts;datestr;%Y-%m-%dT%H:%M:%S%z` - "2025-06-27T15:02:23.082253908Z": `custom_time_index=input_ts;datestr;%Y-%m-%dT%H:%M:%S%.9f%#z` ### Flatten JSON objects The `greptime_identity` pipeline **automatically flattens** nested JSON objects into a single-level structure. This behavior is always enabled and creates separate columns for each nested field using dot notation (e.g., `a.b.c`). #### Controlling flattening depth You can control how deeply nested objects are flattened using the `max_nested_levels` parameter in the `x-greptime-pipeline-params` header. The default value is 10 levels. Here is a sample request: ```shell curl -X "POST" "http://localhost:4000/v1/ingest?db=&table=&pipeline_name=greptime_identity&version=" \ -H "Content-Type: application/x-ndjson" \ -H "Authorization: Basic " \ -H "x-greptime-pipeline-params: max_nested_levels=5" \ -d "$" ``` When the maximum nesting level is reached, any remaining nested structure is converted to a JSON string and stored in a single column. For example, with `max_nested_levels=3`: ```JSON { "a": { "b": { "c": { "d": [1, 2, 3] } } }, "e": [ "foo", "bar" ], "f": { "g": { "h": 123, "i": "hello", "j": { "k": true } } } } ``` Will be flattened to: ```json { "a.b.c": "{\"d\":[1,2,3]}", "e": "[\"foo\",\"bar\"]", "f.g.h": 123, "f.g.i": "hello", "f.g.j": "{\"k\":true}" } ``` Note that: - Arrays at any level are always converted to JSON strings (e.g., `"e"` becomes `"[\"foo\",\"bar\"]"`) - When the nesting level limit is reached (level 3 in this example), the remaining nested objects are converted to JSON strings (e.g., `"a.b.c"` and `"f.g.j"`) - Regular scalar values within the depth limit are stored as their native types (e.g., `"f.g.h"` as integer, `"f.g.i"` as string) --- ## Pipeline Configuration Pipeline is a mechanism in GreptimeDB for parsing and transforming log data. It consists of a unique name and a set of configuration rules that define how log data is formatted, split, and transformed. Currently, we support JSON (`application/json` or `application/x-ndjson`, the latter is preferred) and plain text (`text/plain`) formats as input for log data. These configurations are provided in YAML format, allowing the Pipeline to process data during the log writing process according to the defined rules and store the processed data in the database for subsequent structured queries. ## Input Formats In general, a pipeline receives a set of key-value pairs in the form of a map as its input. The following three formats can all be used as pipeline input: ### JSON Format When the request's Content-Type is `application/json`, the data format should be standard JSON. ```json [ {"message": "hello world", "time": "2024-07-12T16:18:53.048"}, {"message": "hello greptime", "time": "2024-07-12T16:18:53.048"} ] ``` ### NDJSON Format When the request's Content-Type is `application/x-ndjson`, each line is treated as a separate standard JSON object. ```json {"message": "hello world", "time": "2024-07-12T16:18:53.048"} {"message": "hello greptime", "time": "2024-07-12T16:18:53.048"} ``` ### Plain Text Format When the request's Content-Type is `text/plain`, each line is treated as a separate object. The entire line is treated as a string and stored in a map object containing only the `message` field. ``` hello world 2024-07-12T16:18:53.048 hello greptime 2024-07-12T16:18:53.048 ``` The above plain text data will be converted to the following equivalent form: ```json {"message": "hello world 2024-07-12T16:18:53.048"} {"message": "hello greptime 2024-07-12T16:18:53.048"} ``` In other words, when the input is in plain text format, you need to use `message` to refer to the content of each line when writing `Processor` and `Transform` configurations. ## Pipeline Configuration Structure Pipeline consists of four parts: Processors, Dispatcher, Transform, and Table suffix. Processors pre-process input log data. Dispatcher forwards pipeline execution context onto different subsequent pipeline. Transform decides the final datatype and table structure in the database. Table suffix allows storing the data into different tables. - Version is used to state the pipeline configuration format. Although it's optional, it's high recommended to start with version 2. See [here](#transform-in-version-2) for more details. - Processors are used for preprocessing log data, such as parsing time fields and replacing fields. - Dispatcher(optional) is used for forwarding the context into another pipeline, so that the same batch of input data can be divided and processed by different pipeline based on certain fields. - Transform(optional) is used for converting data formats, such as converting string types to numeric types, and specifying indexes. - Table suffix(optional) is used for storing data into different table for later convenience. Here is an example of a simple configuration that includes Processors and Transform: ```yaml version: 2 processors: - urlencoding: fields: - string_field_a - string_field_b method: decode ignore_missing: true dispatcher: field: type rules: - value: http table_suffix: _http pipeline: http - value: db table_suffix: _db transform: - fields: - string_field_a - string_field_b type: string # The written data must include the timestamp field - fields: - reqTimeSec, req_time_sec # epoch is a special field type and must specify precision type: epoch, ms index: timestamp table_suffix: _${string_field_a} ``` Starting from `v0.15`, the GreptimeDB introduce a version `2` format. The main change is the transform process. Refer to [the following documentation](#transform-in-version-2) for detailed changes. ## Processor The Processor is used for preprocessing log data, and its configuration is located under the `processors` section in the YAML file. The Pipeline processes data by applying multiple Processors in sequential order, where each Processor depends on the result of the previous Processor. A Processor consists of a name and multiple configurations, and different types of Processors have different fields in their configuration. We currently provide the following built-in Processors: - `date`: parses formatted time string fields, such as `2024-07-12T16:18:53.048`. - `epoch`: parses numeric timestamp fields, such as `1720772378893`. - `dissect`: splits log data fields. - `gsub`: replaces log data fields. - `join`: merges array-type fields in logs. - `letter`: converts log data fields to letters. - `regex`: performs regular expression matching on log data fields. - `urlencoding`: performs URL encoding/decoding on log data fields. - `csv`: parses CSV data fields in logs. - `json_path`: extracts fields from JSON data. (**deprecated**, please use `vrl` instead) - `json_parse`: parse a field into JSON object. - `simple_extract`: extracts fields from JSON data using simple key. - `digest`: extracts the template from a log message by removing variable content. - `select`: retain(include) or exclude fields from the pipeline context. - `vrl`: runs the [vrl](https://vector.dev/docs/reference/vrl/) script against the pipeline context. - `filter`: filter out lines if the condition check is meet. ### Input and output of Processors Most processors accept a `field` or `fields` parameter, which is a list of fields processed sequentially, as input data. Processors produce one or more output based on the input. For processors that produce a single output, the result will overwrite the original input key in the context. In the following example, after the `letter` processor, an upper-case version of the original string is saved back to the `message` field. ```yaml processors: - letter: fields: - message method: upper ``` We can save the output data to another field, leaving the original field unchanged. In the following example, we still use the `message` field as the input of the `letter` processor, but saving the output to a new field named `upper_message`. ```yaml processors: - letter: fields: - key: message rename_to: upper_message method: upper ``` This renaming syntax also has a shortcut form: simply separate the two field names with a `,`. Here is an example of this shortcut: ```yaml processors: - letter: fields: - message, upper_message method: upper ``` There are mainly two reasons why renaming is needed: 1. Leaving the original field unchanged, so it can be reused in more than one processors, or be saved into the database as well. 2. Unifying naming. For example rename camel-case names to snake-case names for consistency. ### `date` The `date` processor is used to parse time fields. Here's an example configuration: ```yaml processors: - date: fields: - time formats: - '%Y-%m-%d %H:%M:%S%.3f' ignore_missing: true timezone: 'Asia/Shanghai' ``` In the above example, the configuration of the `date` processor includes the following fields: - `fields`: A list of time field names to be parsed. - `formats`: Time format strings, supporting multiple format strings. Parsing is attempted in the order provided until successful. You can find reference [here](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) for formatting syntax. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. - `timezone`: Time zone. Use the time zone identifiers from the [tz_database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) to specify the time zone. Defaults to `UTC`. ### `epoch` The `epoch` processor is used to parse timestamp fields. Here's an example configuration: ```yaml processors: - epoch: fields: - reqTimeSec resolution: millisecond ignore_missing: true ``` In the above example, the configuration of the `epoch` processor includes the following fields: - `fields`: A list of timestamp field names to be parsed. - `resolution`: Timestamp precision, supports `s`, `sec`, `second`, `ms`, `millisecond`, `milli`, `us`, `microsecond`, `micro`, `ns`, `nanosecond`, `nano`. Defaults to `ms`. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. ### `dissect` The `dissect` processor is used to split log data fields. Here's an example configuration: ```yaml processors: - dissect: fields: - message patterns: - '%{key1} %{key2}' ignore_missing: true append_separator: '-' ``` In the above example, the configuration of the `dissect` processor includes the following fields: - `fields`: A list of field names to be split, does not support field renaming. - `patterns`: The dissect pattern for splitting. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. - `append_separator`: Specifies the separator for concatenating multiple fields with same field name together. Defaults to an empty string. See `+` modifier below. #### Dissect pattern Similar to Logstash's dissect pattern, the dissect pattern consists of `%{key}`, where `%{key}` is a field name. For example: ``` "%{key1} %{key2} %{+key3} %{+key4/2} %{key5->} %{?key6}" ``` #### Dissect modifiers The dissect pattern supports the following modifiers: | Modifier | Description | Example | | ------------ | ------------------------------------------------------ | --------------------- | | `+` | Concatenates two or more fields together | `%{+key} %{+key}` | | `+` and `/n` | Concatenates two or more fields in the specified order | `%{+key/2} %{+key/1}` | | `->` | Ignores any repeating characters on the right side | `%{key1->} %{key2->}` | | `?` | Ignores matching values | `%{?key}` | #### `dissect` examples For example, given the following log data: ``` "key1 key2 key3 key4 key5 key6" ``` Using the following Dissect pattern: ``` "%{key1} %{key2} %{+key3} %{+key3/2} %{key5->} %{?key6}" ``` The result will be: ``` { "key1": "key1", "key2": "key2", "key3": "key3 key4", "key5": "key5" } ``` ### `gsub` The `gsub` processor is used to replace values in log data fields. Here's an example configuration: ```yaml processors: - gsub: fields: - message pattern: 'old' replacement: 'new' ignore_missing: true ``` In the above example, the configuration of the `gsub` processor includes the following fields: - `fields`: A list of field names to be replaced. - `pattern`: The string to be replaced. Supports regular expressions. - `replacement`: The string to replace with. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. ### `join` The `join` processor is used to merge Array-type fields in log data. Here's an example configuration: ```yaml processors: - join: fields: - message separator: ',' ignore_missing: true ``` In the above example, the configuration of the `join` processor includes the following fields: - `fields`: A list of field names to be merged. Note that all the fields mentioned are already of array type. Each field will have its own array merged separately. The content of multiple fields will not be combined. - `separator`: The separator for the merged result. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. #### `join` example For example, given the following log data: ```json { "message": ["a", "b", "c"] } ``` Using the following configuration: ```yaml processors: - join: fields: - message separator: ',' ``` The result will be: ```json { "message": "a,b,c" } ``` ### `letter` The `letter` processor is used to convert the case of characters in log data fields. Here's an example configuration: ```yaml processors: - letter: fields: - message method: upper ignore_missing: true ``` In the above example, the configuration of the `letter` processor includes the following fields: - `fields`: A list of field names to be transformed. - `method`: The transformation method, supports `upper`, `lower`, `capital`. Defaults to `lower`. Note the `capital` only changes the first letter to uppercase. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. ### `regex` The `regex` processor is used to perform regular expression matching on log data fields. Here's an example configuration: ```yaml processors: - regex: fields: - message patterns: - ':(?[0-9])' ignore_missing: true ``` In the above example, the configuration of the `regex` processor includes the following fields: - `fields`: A list of field names to be matched. If you rename the field, the renamed fields will be combined with the capture groups in `patterns` to generate the result name. - `pattern`: The regular expression pattern to match. Named capture groups are required to extract corresponding data from the respective field. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. #### Rules for named capture groups in regex The `regex` processor supports the syntax `(?...)` to define named capture groups. The data will be processed into the following format: ```json { "_": "" } ``` For example, if the field name specified in the `regex` processor is `message`, and the corresponding content is `"[ERROR] error message"`, you can set the pattern as `\[(?[A-Z]+)\] (?.+)`, and the data will be processed as: ```json { "message_level": "ERROR", "message_content": "error message" } ``` ### `urlencoding` The `urlencoding` processor is used to perform URL encoding on log data fields. Here's an example configuration: ```yaml processors: - urlencoding: fields: - string_field_a - string_field_b method: decode ignore_missing: true ``` In the above example, the configuration of the `urlencoding` processor includes the following fields: - `fields`: A list of field names to be encoded. - `method`: The encoding method, supports `encode`, `decode`. Defaults to `encode`. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. ### `csv` The `csv` processor is used to parse CSV-type fields in log data that do not have a header. Here's an example configuration: ```yaml processors: - csv: fields: - message target_fields: ip,method,path separator: ',' quote: '"' trim: true ignore_missing: true ``` In the above example, the configuration of the `csv` processor includes the following fields: - `fields`: A list of field names to be parsed. - `target_fields`: Comma-separated names of the output fields. Parsed values are assigned to these fields in column order, and columns beyond the number of target fields are discarded. If not set, the processor produces no output fields. - `empty_value`: The value assigned to target fields that have no corresponding column in the record. If not set, these fields are `null`. - `separator`: The separator. - `quote`: The quotation mark. - `trim`: Whether to trim whitespace. Defaults to `false`. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. ### `json_path` (deprecated) :::danger Deprecated Feature The `json_path` processor is deprecated. The `vrl` processor covers its use cases and provides more flexible JSON field extraction, so use `vrl` in new pipelines. ::: The `json_path` processor is used to extract fields from JSON data. Here's an example configuration: ```yaml processors: - json_path: fields: - complex_object json_path: "$.shop.orders[?(@.active)].id" ignore_missing: true result_index: 1 ``` In the above example, the configuration of the `json_path` processor includes the following fields: - `fields`: A list of field names to be extracted. - `json_path`: The JSON path to extract. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. - `result_index`: Specifies the index of the value in the extracted array to be used as the result value. By default, all values are included. The extracted value of the processor is an array containing all the values of the path. If an index is specified, the corresponding value in the extracted array will be used as the final result. #### JSON path syntax The JSON path syntax is based on the [jsonpath-rust](https://github.com/besok/jsonpath-rust) library. At this stage we only recommend using some simple field extraction operations to facilitate the extraction of nested fields to the top level. #### `json_path` example For example, given the following log data: ```json { "product_object": { "hello": "world" }, "product_array": [ "hello", "world" ], "complex_object": { "shop": { "orders": [ { "id": 1, "active": true }, { "id": 2 }, { "id": 3 }, { "id": 4, "active": true } ] } } } ``` Using the following configuration: ```yaml processors: - json_path: fields: - product_object, object_target json_path: "$.hello" result_index: 0 - json_path: fields: - product_array, array_target json_path: "$.[1]" result_index: 0 - json_path: fields: - complex_object, complex_target_1 json_path: "$.shop.orders[?(@.active)].id" - json_path: fields: - complex_target_1, complex_target_2 json_path: "$.[1]" result_index: 0 - json_path: fields: - complex_object, complex_target_3 json_path: "$.shop.orders[?(@.active)].id" result_index: 1 transform: - fields: - object_target - array_target type: string - fields: - complex_target_3 - complex_target_2 type: uint32 - fields: - complex_target_1 type: json ``` The result will be: ```json { "object_target": "world", "array_target": "world", "complex_target_3": 4, "complex_target_2": 4, "complex_target_1": [1, 4] } ``` ### `json_parse` `json_parse`, as its name suggests, parses a string field into a JSON object. Here's an example configuration: ```yaml processors: - json_parse: fields: - complex_object, target_field ignore_missing: true ``` In the above example, the configuration of the `json_parse` processor includes the following fields: - `fields`: For each field, the first key represents the entry string in the context, the second key represents the output key name. If the second key is not present, it uses the first key as the output key name too, which means in-place replacement of the original value. The example above means parsing `complex_object` and put the output into `target_field`. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. #### `json_parse` example For example, given the following log data: ```json { "product_object": "{\"hello\":\"world\"}", } ``` Using the following configuration: ```yaml processors: - json_parse: fields: - product_object transform: - fields: - product_object type: json ``` After the `json_parse` processor runs, the `product_object` field in the context becomes a JSON object: ```json { "product_object": { "hello": "world" } } ``` The `transform` section then maps the parsed object to the `json` type, so it is stored as a JSON column named `product_object` in the table. ### `simple_extract` While `json_path` processor is capable of extracting fields from JSON objects using complex expressions, it's relatively slow and costly. The `simple_extract` processor offers a simple way to extract fields by using just key names. Here's an example configuration: ```yaml processors: - simple_extract: fields: - complex_object, target_field key: "shop.name" ignore_missing: true ``` In the above example, the configuration of the `simple_extract` processor includes the following fields: - `fields`: For each field, the first key represents the entry JSON object in the context, the second key represents the output key name. The example above means extracting from `complex_object` and put the output into `target_field`. - `key`: The extracting key, using `x.x` format, each `.` represents a new nested layer. - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. #### `simple_extract` example For example, given the following log data: ```json { "product_object": { "hello": "world" }, "product_array": [ "hello", "world" ], "complex_object": { "shop": { "name": "some_shop_name" } } } ``` Using the following configuration: ```yaml processors: - simple_extract: fields: - complex_object, shop_name key: "shop.name" transform: - fields: - shop_name type: string ``` The result will be: ```json { "shop_name": "some_shop_name" } ``` ### `digest` The `digest` processor is used to extract the template from a log message by removing variable content, such as numbers, UUIDs, IP addresses, quoted strings, and bracketed text. The digested result can be useful for log template grouping and analysis. Here's an example configuration: ```yaml processors: - digest: fields: - message presets: - numbers - uuid - ip - quoted - bracketed regex: - "foobar" ignore_missing: true ``` In the above example, the configuration of the `digest` processor includes the following fields: - `fields`: A list of field names to be digested. The digested result will be stored in a new field with suffix `_digest`. - `presets`: A list of preset patterns to remove. The following patterns are supported: - `numbers`: Matches numeric sequences - `uuid`: Matches UUID strings like `123e4567-e89b-12d3-a456-426614174000` - `ip`: Matches IPv4/IPv6 addresses with optional port numbers - `quoted`: Matches text within single/double quotes (including various Unicode quotes) - `bracketed`: Matches text within various types of brackets (including various Unicode brackets) - `regex`: A list of custom regex patterns to remove - `ignore_missing`: Ignores the case when the field is missing. Defaults to `false`. If the field is missing and this configuration is set to `false`, an exception will be thrown. #### `digest` example For example, given the following log data: ```json { "message": "User 'john.doe' from [192.168.1.1] accessed resource 12345 with UUID 123e4567-e89b-12d3-a456-426614174000" } ``` Using the following configuration: ```yaml processors: - digest: fields: - message presets: - numbers - uuid - ip - quoted - bracketed ``` The result will be: ```json { "message": "User 'john.doe' from [192.168.1.1] accessed resource 12345 with UUID 123e4567-e89b-12d3-a456-426614174000", "message_digest": "User from accessed resource with UUID " } ``` The digested template can be used to group similar log messages together or analyze log patterns, even when the variable content differs. For example, all these log messages would generate the same template: - `User 'alice' from [10.0.0.1] accessed resource 54321 with UUID 987fbc97-4bed-5078-9141-2791ba07c9f3` - `User 'bob' from [2001:0db8::1] accessed resource 98765 with UUID 550e8400-e29b-41d4-a716-446655440000` ### `select` The `select` processor is used to retain or exclude fields from the pipeline execution context. Starting from `v0.15` release, we are introducing [`auto-transform`](#auto-transform) for simplicity. The `auto-transform` mode will try to preserve all fields from the pipeline execution context. The `select` processor can be used here to select fields to include or exclude, which will reflects the final table schema if `auto-transform` is used. The configuration options for `select` is simple: - `type`(optional) - `include`(default): only keeps the selected fields - `exclude`: removes the selected fields from the current context - `fields`: fields selected from the pipeline execution context Here is an example: ```YAML processors: - dissect: fields: - message patterns: - "%{+ts} %{+ts} %{http_status_code} %{content}" - date: fields: - ts formats: - "%Y-%m-%d %H:%M:%S%.3f" - select: fields: - http_status_code - ts ``` With `dissect` and `date` processor, there are four fields in the context: `ts`, `http_status_code`, `content` and the original `message`. Without the `select` processor, all four fields are preserved. The `select` processor here selects `http_status_code` and `ts` fields to include (by default), which effectively removes `content` and `message` in the pipeline execution context, resulting the `http_status_code` and `ts` to be preserved into the database. The above example can also be done in the following `select` processor's configuration: ```YAML - select: type: exclude fields: - content - message ``` ### `vrl` :::warning Experimental Feature This experimental feature may contain unexpected behavior, have its functionality change in the future. ::: The `vrl` processor runs a VRL script against the pipeline context. It supports transformations that cannot be expressed with the built-in processors, but executing a script uses more resources than a built-in processor. See the [VRL reference](https://vector.dev/docs/reference/vrl/) for language syntax. The `vrl` processor takes one configuration field, `source`: ```YAML processors: - date: field: time formats: - "%Y-%m-%d %H:%M:%S%.3f" ignore_missing: true - vrl: source: | .from_source = "channel_2" cond, err = .id1 > .id2 if (cond) { .from_source = "channel_1" } del(.id1) del(.id2) . ``` The configuration uses a `|` to start a multi-line content in the YAML file. The whole script is then written below. Some notes regarding the `vrl` processor: 1. The script must end with either a single `.` (returning the whole context as one row) or an array of objects (one-to-many expansion, where each element becomes a separate row). 2. The returning value of the vrl script should not contain any regex-type variables. They can be used in the script, but have to be `del`ed before returning. 3. Due to type conversion between pipeline's value type and vrl's, the value type that comes out of the vrl script will be the ones with max capacity, meaning `i64`, `f64`, and `Timestamp::nanoseconds`. You can use `vrl` processor to set [table options](./write-log-api.md#set-table-options) while writing logs. #### One-to-Many Expansion The `vrl` processor supports **one-to-many** expansion: when the script returns an array, each element of the array is written as a separate row. This is useful when a single incoming log record contains multiple events that should be stored as individual rows. Each element in the returned array must be an object. Returning an empty array produces zero rows (the record is dropped). Example: expand a log record containing multiple events into individual rows: ```YAML processors: - epoch: field: timestamp resolution: ms - vrl: source: | events = del(.events) base_host = del(.host) base_ts = del(.timestamp) map_values(array!(events)) -> |event| { { "host": base_host, "event_type": event.type, "event_value": event.value, "timestamp": base_ts } } transform: - field: host type: string - field: event_type type: string - field: event_value type: int32 - field: timestamp type: timestamp, ms index: timestamp ``` Given the input: ```json { "host": "server1", "timestamp": 1716668197217, "events": [ {"type": "cpu", "value": 80}, {"type": "memory", "value": 60}, {"type": "disk", "value": 45} ] } ``` The pipeline produces three rows, one for each event in the array. You can also set a per-row table suffix by including a `greptime_table_suffix` field in each array element. This routes different rows to different tables: ```YAML - vrl: source: | events = del(.events) base_ts = del(.timestamp) map_values(array!(events)) -> |event| { suffix = "_" + string!(event.category) { "name": event.name, "value": event.value, "timestamp": base_ts, "greptime_table_suffix": suffix } } ``` ### `filter` The `filter` processor can filter out unneeded lines when the condition is meet. Here is a simple example of the `filter` processor: ```YAML processors: - date: field: time formats: - "%Y-%m-%d %H:%M:%S%.3f" - filter: field: name mode: simple match_op: in case_insensitive: true targets: - John - Wick transform: - field: name type: string - field: time type: time index: timestamp ``` The `filter` processor is configured to check the `name` variable's value from the pipeline context. If the value of `name` is found within the target list `['John', 'Wick']`, the input line is considered a match and will be discarded, preventing it from being written to the database. The `filter` processor takes the following options: 1. `field`(or `fields`): the variable that is used for checking; it can be a list of variables, any match will do the filter. 2. `mode`(optional): default to `simple`, which means simple string matching. This field is here for future extension. 3. `match_op`(optional): if the mode is `simple`, this option can be `in` or `not_in`, which means if the target list contains the input value. Default to `in`. 4. `case_insensitive`(optional): default to `true`. 5. `targets`: the target string lists that are compared against the variable. ## Transform Transform is used to convert data formats and specify indexes upon columns. It is located under the `transform` section in the YAML file. Starting from `v0.15`, GreptimeDB is introducing version 2 format and auto-transform to largely simplify the configuration. See below for details. A Transform consists of one or more configurations, and each configuration contains the following fields: - `fields`: A list of field names to be transformed. - `type`: The target transformation type in the database. - `index`(optional): The index type. - `tag`(optional): Specify the field to be a tag field. - `on_failure`(optional): Handling method for transformation failures. - `default`(optional): Default value. ### Transform in version 2 Originally, you have to manually specify all the fields in the transform section for them to be persisted in the database. If a field is not specify in the transform, it will be discards. With the number of field growing, this can make the configuration both tedious and error-prone. Starting from `v0.15`, GreptimeDB introduces a new transform mode which make it easier to write pipeline configuration. You only set necessary fields in the transform section, specifying particular datatype and index for them; the rest of the fields from the pipeline context are set automatically by the pipeline engine. With the `select` processor, you can decide what field is wanted and what isn't in the final table. However, this is a breaking change to the existing pipeline configuration files. If you has already used pipeline with `dissect` or `regex` processors, after upgrading the database, the original message string, which is still in the pipeline context, gets immediately inserted into the database and there's no way to stop this behavior. Therefore, GreptimeDB introduces the concept of version to decide which transform mode you want to use, just like the version in a Docker Compose file. Here is an example: ```YAML version: 2 processors: - date: field: input_str formats: - "%Y-%m-%dT%H:%M:%S%.3fZ" transform: - field: input_str, ts type: time, ms index: timestamp ``` Simply add a `version: 2` line at the top of the config file, and the pipeline engine will run the transform in combined mode: 1. Process all written transform rules sequentially. 2. Write all fields of the pipeline context to the final table. Note: - The transform section **must contains one timestamp index field**. - The transform process in the version 2 will consume the original field in the pipeline context, so you can't transform the same field twice. ### Auto-transform The transform configuration in version 2 format is already a large simplification over the original transform. However, there are times when you might want to combine the power of processors with the ease of using `greptime_identity`, writing no transform code, letting the pipeline engine auto infer and persist the data. Now it is possible in custom pipelines. If no transform section is specified, the pipeline engine will attempt to infer the datatype of the fields from the pipeline context and preserve them into the database, much like what the `identity_pipeline` does. To create a table in GreptimeDB, a timestamp index column must be specified. In this case, the pipeline engine will try to find a field of type `timestamp` in the context and set it as the time index column. A `timestamp` field is produced by a `date` or `epoch` processor, so at least one `date` or `epoch` processor must be defined in the processor's section. Additionally, only one `timestamp` field is allowed, multiple `timestamp` fields would lead to an error due to ambiguity. For example, the following pipeline configuration is now valid. ```YAML version: 2 processors: - dissect: fields: - message patterns: - '%{ip_address} - %{username} [%{timestamp}] "%{http_method} %{request_line} %{protocol}" %{status_code} %{response_size}' ignore_missing: true - date: fields: - timestamp formats: - "%d/%b/%Y:%H:%M:%S %z" ``` And it result to the following table schema: ``` mysql> desc auto_trans; +---------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------+---------------------+------+------+---------+---------------+ | timestamp | TimestampNanosecond | PRI | NO | | TIMESTAMP | | host | String | | YES | | FIELD | | http_method | String | | YES | | FIELD | | ip_address | String | | YES | | FIELD | | message | String | | YES | | FIELD | | protocol | String | | YES | | FIELD | | request_line | String | | YES | | FIELD | | response_size | String | | YES | | FIELD | | service | String | | YES | | FIELD | | source_type | String | | YES | | FIELD | | status_code | String | | YES | | FIELD | | username | String | | YES | | FIELD | +---------------+---------------------+------+------+---------+---------------+ 12 rows in set (0.03 sec) ``` We can see that all fields are preserved, including the original `message` field. Note that all fields are saved as `String` type, and the `timestamp` field is automatically set as the time index. ### The `fields` field Each field name is a string. `fields` in transform can also use renaming, refer to the docs [here](#input-and-output-of-processors). The final name of the field will be used as the column name in the database table. ### The `type` field GreptimeDB currently provides the following built-in transformation types: - `int8`, `int16`, `int32`, `int64`: Integer types. - `uint8`, `uint16`, `uint32`, `uint64`: Unsigned integer types. - `float32`, `float64`: Floating-point types. - `boolean`: Boolean type. - `string`: String type. - `time`: Time type, which will be converted to GreptimeDB `timestamp(9)` type. - `epoch`: Timestamp type, which will be converted to GreptimeDB `timestamp(n)` type. The value of `n` depends on the precision of the epoch. When the precision is `s`, `n` is 0; when the precision is `ms`, `n` is 3; when the precision is `us`, `n` is 6; when the precision is `ns`, `n` is 9. - `json`: JSON type. The field value must be a JSON object or array, such as one produced by the [`json_parse`](#json_parse) processor, and it is stored as a JSON column in the table. Scalar values (for example, strings, numbers) cannot be converted to the `json` type. - `json2`: [JSON2](/user-guide/logs/json2.md) type. The field may be missing or `null`. A non-null value must be a non-empty JSON object, such as one produced by the [`json_parse`](#json_parse) processor. Root arrays and scalar values cannot be converted to `json2`. If a field obtains an illegal value during the transformation process, the Pipeline will throw an exception. For example, when converting a string `abc` to an integer, an exception will be thrown because the string is not a valid integer. #### JSON2 type hints :::note JSON2 is currently in Beta, and some capabilities are still being improved. ::: For JSON2 behavior, query syntax, and current limitations, see the [JSON2 type documentation](/user-guide/logs/json2.md). When a pipeline creates the destination column, the short form creates a JSON2 column without type hints: ```yaml transform: - field: payload type: json2 ``` To declare type hints for the new JSON2 column, use the object form: ```yaml transform: - field: payload type: json2: - path: user.id type: int64 - path: 'attrs."http.status_code"' type: string ``` Each type hint supports the following fields: - `path` (required): The JSON subpath in dot notation. Wrap a path segment in double quotes when the JSON key itself contains a dot. - `type` (required): One of `string`, `int64`, `uint64`, `float64`, or `boolean`. - `nullable` (optional): Whether the path can be missing or `null`. Defaults to `true`. - `default` (optional): A scalar value of the declared type to use when the path is missing. If the destination JSON2 column already exists, GreptimeDB encodes the value using the settings stored in that column instead of the inline hints. This also applies when [`table_suffix`](#table-suffix) routes records to different tables. Values that violate the applicable type hints follow the transform's [`on_failure`](#the-on_failure-field) setting. ### The `index` field The `Pipeline` will write the processed data to the automatically created table in GreptimeDB. To improve query efficiency, GreptimeDB creates indexes for certain columns in the table. The `index` field is used to specify which fields need to be indexed. For information about GreptimeDB index types, please refer to the [Data Index](/user-guide/manage-data/data-index.md) documentation. GreptimeDB supports the following four types of index for fields: - `timestamp`: Specifies a column as a timestamp index column. - `inverted`: Specifies a column to use the inverted index type. - `fulltext`: Specifies a column to use the fulltext index type. The column must be of string type. - `skipping`: Specifies a column to use the skipping index type. The `index` field supports both a shorthand string form and a detailed object form: ```yaml index: fulltext index: type: fulltext options: analyzer: Chinese case_sensitive: true backend: bloom granularity: 2048 false_positive_rate: 0.02 ``` The shorthand string form remains supported and uses the default index options. In the object form, `type` is required and `options` is optional. Only `fulltext` and `skipping` support `options`. Option names and validation rules are the same as the corresponding SQL index options described in [Data Index](/user-guide/manage-data/data-index.md). Each option value must be a scalar YAML value. When `index` field is not provided, GreptimeDB doesn't create index on the column. In GreptimeDB, a table must include exact one column of type `timestamp` as the time index column. Therefore, a Pipeline can have only one time index column. #### The Timestamp column Specify which field is the timestamp index column using `index: timestamp`. Refer to the [Transform Example](#transform-example) below for syntax. #### The Inverted Index Specify which field uses the inverted index. Refer to the [Transform Example](#transform-example) below for syntax. #### The Fulltext Index Specify which field will be used for full-text search using `index: fulltext`. This index improves the performance of [log search](/user-guide/logs/fulltext-search.md). Use the detailed form when you need to set fulltext index options: ```yaml - field: message type: string index: type: fulltext options: analyzer: Chinese case_sensitive: true backend: bloom granularity: 2048 false_positive_rate: 0.02 ``` #### The Skipping Index Specify which field uses the skipping index. This index speeds up the query on high cardinality fields but consumes far less storage for building index files. Unlike `fulltext`, `skipping` is not limited to string columns. Use the detailed form when you need to set skipping index options: ```yaml - field: trace_id type: int64 index: type: skipping options: granularity: 1024 false_positive_rate: 0.05 type: BLOOM ``` ### The `tag` field The `Pipeline` can also specify a tag field manually. For information about tag field, please refer to the [Data Model](/user-guide/concepts/data-model.md) documentation. Refer to the [Transform Example](#transform-example) below for syntax. ### The `on_failure` field The `on_failure` field is used to specify the handling method when a transformation fails. It supports the following methods: - `ignore`: Ignore the failed field and do not write it to the database. - `default`: Write the default value. The default value is specified by the `default` field. ### The `default` field The `default` field is used to specify the default value when a transformation fails. ### Transform Example For example, with the following log data: ```json { "num_field_a": "3", "string_field_a": "john", "string_field_b": "It was snowing when he was born.", "time_field_a": 1625760000 } ``` Using the following configuration: ```yaml transform: - fields: - string_field_a, name type: string index: skipping tag: true - fields: - num_field_a, age type: int32 index: inverted - fields: - string_field_b, description type: string index: fulltext - fields: - time_field_a, born_time type: epoch, s index: timestamp ``` The result will be: ``` { "name": "john", "age": 3, "description": "It was snowing when he was born.", "born_time": 2021-07-08 16:00:00 } ``` ## Dispatcher The pipeline dispatcher routes requests to other pipelines based on configured field values. It is useful when multiple log types share a single source and need to be stored in separate tables with different structures. A sample configuration is like: ```yaml dispatcher: field: type rules: - value: http table_suffix: _http pipeline: http - value: db table_suffix: _db ``` The dispatcher runs after processors and before transformations. It routes data to the next pipeline, where the defined processors will execute. You can specify a `field` and `rules` for routing. If the field value matches a rule's `value`, the data is routed to the specified `pipeline`. If no pipeline is defined, the current data structure is used as the table structure. In the example configuration above, if the `type` of input data is `http`, we will call `http` as next pipeline. And if `type` is `db`, we use current data structure to store the data. The target table name is determined by `table_suffix`, appended directly to the current `table`. If you want a separator such as an underscore `_`, include it in `table_suffix` explicitly. For example, if the table is `applogs` and it matches the `http` rule above, data is stored in `applogs_http`. If no rules match, data is transformed by the current pipeline's transformations. ## Table suffix :::warning Experimental Feature This experimental feature may contain unexpected behavior, have its functionality change in the future. ::: There are cases where you want to split and insert log data into different target table based on some certain values of input data. For example, if you want to divide and store the log data based on the application where the log is produced, thus adding an app name suffix to the target table. A sample configuration is like: ```yaml table_suffix: _${app_name} ``` The syntax is simple: use `${}` to include the variable in the pipeline execution context. The variable can be directly from the input data or a product of former process. After the table suffix is formatted, the whole string will be added to the input table name. Note: 1. The variable must be an integer number or a string type of data. 2. If any error occurs in runtime(e.g: the variable is missing or not a valid type), the input table name will be used. Here is an example of how it works. The input data is like following: ```JSON [ {"type": "db"}, {"type": "http"}, {"t": "test"} ] ``` The input table name is `persist_app`, and the pipeline config is like ```YAML table_suffix: _${type} ``` These three lines of input log will be inserted into three tables: 1. `persist_app_db` 2. `persist_app_http` 3. `persist_app`, for it doesn't have a `type` field, thus the default table name will be used. --- ## APIs for Writing Logs Before writing logs, please read the [Pipeline Configuration](/user-guide/logs/use-custom-pipelines.md#upload-the-pipeline) to complete the configuration setup and upload. ## HTTP API You can use the following command to write logs via the HTTP interface: ```shell curl -X "POST" "http://localhost:4000/v1/ingest?db=&table=&pipeline_name=&version=&skip_error=" \ -H "Content-Type: application/x-ndjson" \ -H "Authorization: Basic " \ -d "$" ``` ### Request parameters This interface accepts the following parameters: - `db`: The name of the database. - `table`: The name of the table. - `pipeline_name`: The name of the [pipeline](./pipeline-config.md). - `version`: The version of the pipeline. Optional, default use the latest one. - `skip_error`: Whether to skip errors when writing logs. Optional, defaults to `false`. When set to `true`, GreptimeDB will skip individual log entries that encounter errors and continue processing the remaining logs. This prevents the entire request from failing due to a single problematic log entry. ### `Content-Type` and body format GreptimeDB uses `Content-Type` header to decide how to decode the payload body. Currently the following two format is supported: - `application/json`: this includes normal JSON format and NDJSON format. - `application/x-ndjson`: specifically uses NDJSON format, which will try to split lines and parse for more accurate error checking. - `text/plain`: multiple log lines separated by line breaks. #### `application/json` and `application/x-ndjson` format Here is an example of JSON format body payload ```JSON [ {"message":"127.0.0.1 - - [25/May/2024:20:16:37 +0000] \"GET /index.html HTTP/1.1\" 200 612 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\""}, {"message":"192.168.1.1 - - [25/May/2024:20:17:37 +0000] \"POST /api/login HTTP/1.1\" 200 1784 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36\""}, {"message":"10.0.0.1 - - [25/May/2024:20:18:37 +0000] \"GET /images/logo.png HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0\""}, {"message":"172.16.0.1 - - [25/May/2024:20:19:37 +0000] \"GET /contact HTTP/1.1\" 404 162 \"-\" \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1\""} ] ``` Note the whole JSON is an array (log lines). Each JSON object represents one line to be processed by Pipeline engine. The name of the key in JSON objects, which is `message` here, is used as field name in Pipeline processors. For example: ```yaml processors: - dissect: fields: # `message` is the key in JSON object - message patterns: - '%{ip_address} - - [%{timestamp}] "%{http_method} %{request_line}" %{status_code} %{response_size} "-" "%{user_agent}"' ignore_missing: true # rest of the file is ignored ``` We can also rewrite the payload into NDJSON format like following: ```JSON {"message":"127.0.0.1 - - [25/May/2024:20:16:37 +0000] \"GET /index.html HTTP/1.1\" 200 612 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\""} {"message":"192.168.1.1 - - [25/May/2024:20:17:37 +0000] \"POST /api/login HTTP/1.1\" 200 1784 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36\""} {"message":"10.0.0.1 - - [25/May/2024:20:18:37 +0000] \"GET /images/logo.png HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0\""} {"message":"172.16.0.1 - - [25/May/2024:20:19:37 +0000] \"GET /contact HTTP/1.1\" 404 162 \"-\" \"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1\""} ``` Note the outer array is eliminated, and lines are separated by line breaks instead of `,`. #### `text/plain` format GreptimeDB accepts plain-text logs with `Content-Type: text/plain`, so text produced by a log source can be ingested directly. The equivalent body payload of previous example is like following: ```plain 127.0.0.1 - - [25/May/2024:20:16:37 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" 192.168.1.1 - - [25/May/2024:20:17:37 +0000] "POST /api/login HTTP/1.1" 200 1784 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36" 10.0.0.1 - - [25/May/2024:20:18:37 +0000] "GET /images/logo.png HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0" 172.16.0.1 - - [25/May/2024:20:19:37 +0000] "GET /contact HTTP/1.1" 404 162 "-" "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1" ``` To send the same payload as plain text, set the `Content-Type` header to `text/plain`. Please note that, unlike JSON format, where the input data already have key names as field names to be used in Pipeline processors, `text/plain` format just gives the whole line as input to the Pipeline engine. In this case we use `message` as the field name to refer to the input line, for example: ```yaml processors: - dissect: fields: # use `message` as the field name - message patterns: - '%{ip_address} - - [%{timestamp}] "%{http_method} %{request_line}" %{status_code} %{response_size} "-" "%{user_agent}"' ignore_missing: true # rest of the file is ignored ``` It is recommended to use `dissect` or `regex` processor to split the input line into fields first and then process the fields accordingly. ## Set Table Options The table options need to be set in the pipeline configurations. Starting from `v0.15`, the pipeline engine recognizes certain variables, and can set corresponding table options based on the value of the variables. Combined with the `vrl` processor, these variables can set table options from input data during pipeline execution. Here is a list of supported common table option variables: - `greptime_auto_create_table` - `greptime_ttl` - `greptime_append_mode` - `greptime_merge_mode` - `greptime_physical_table` - `greptime_skip_wal` Please refer to [table options](/reference/sql/create.md#table-options) for the detailed explanation of each option. Here are some pipeline specific variables: - `greptime_table_suffix`: add suffix to the destined table name. Let's use the following pipeline file to demonstrate: ```YAML processors: - date: field: time formats: - "%Y-%m-%d %H:%M:%S%.3f" ignore_missing: true - vrl: source: | .greptime_table_suffix, err = "_" + .id .greptime_ttl = "1d" . ``` In the vrl script, we set the table suffix variable with the input field `.id`(leading with an underscore), and set the ttl to `1d`. Then we run the ingestion using the following JSON data. ```JSON { "id": "2436", "time": "2024-05-25 20:16:37.217" } ``` If the table name in the request is `d_table`, the data is written to `d_table_2436` with a TTL of one day. ## Examples Please refer to the "Writing Logs" section in the [Quick Start](/user-guide/logs/quick-start.md#direct-http-ingestion) and [Using Custom Pipelines](/user-guide/logs/use-custom-pipelines.md#ingest-logs-using-the-pipeline) guide for examples. --- ## ADMIN `ADMIN` is used to run administration functions. ```sql ADMIN function(arg1, arg2, ...) ``` ## Admin Functions GreptimeDB provides some administration functions to manage the database and data: * `flush_table(table_name)` to flush a table's memtables into SST file by table name. * `flush_region(region_id)` to flush a region's memtables into SST file by region id. Find the region id through [PARTITIONS](./information-schema/partitions.md) table. * `discard_unflushed(table_name_or_region_id)` to permanently discard unflushed data from every physical region of a table or from one region. * `compact_table(table_name, [type], [options])` to schedule a compaction task for a table by table name, read [compaction](/user-guide/deployments-administration/manage-data/compaction.md#strict-window-compaction-strategy-swcs-and-manual-compaction) for more details. * `compact_region(region_id)` to schedule a compaction task for a region by region id. * `build_index(table_name)` to build missing physical indexes for a table's existing SST files after adding or changing index definitions. * `migrate_region(region_id, from_peer, to_peer, [timeout])` to migrate regions between datanodes, please read the [Region Migration](/user-guide/deployments-administration/manage-data/region-migration.md). * `procedure_state(procedure_id)` to query a procedure state by its id. * `flush_flow(flow_name)` to flush a flow's output into the sink table. * `reconcile_table(table_name)` to reconcile the metadata inconsistency of a table, read [table reconciliation](/user-guide/deployments-administration/maintenance/table-reconciliation.md) for more details. * `reconcile_database(database_name)` to reconcile the metadata inconsistency of all tables in a database, read [table reconciliation](/user-guide/deployments-administration/maintenance/table-reconciliation.md) for more details. * `reconcile_catalog()` to reconcile the metadata inconsistency of all tables in the entire cluster, read [table reconciliation](/user-guide/deployments-administration/maintenance/table-reconciliation.md) for more details. * `gc_table(table_name, [full_file_listing])` to garbage collect orphaned SST files in object storage for a dropped table. Returns the number of processed regions. The optional `full_file_listing` boolean (default `false`) enables a thorough scan of all files when set to `true`. * `gc_regions(region_id1, ..., region_idN, [full_file_listing])` to garbage collect orphaned SST files in object storage for one or more specific regions by their region ids. Returns the number of processed regions. The optional `full_file_listing` boolean (default `false`) enables a thorough scan of all files when set to `true`. * `purge_table(table_name)` to permanently purge a [soft-dropped table](/enterprise/soft-drop.md). The table name can be unqualified, schema-qualified, or fully qualified. This function is only available in the GreptimeDB Enterprise database through the `ADMIN` statement. For example: ```sql -- Flush the table test -- admin flush_table("test"); -- Discard unflushed data from all physical regions of table test -- admin discard_unflushed("test"); -- Discard unflushed data from one region -- admin discard_unflushed(4398046511104); -- Schedule a compaction for table test with default parallelism (1) -- admin compact_table("test"); -- Schedule a regular compaction with parallelism set to 2 -- admin compact_table("test", "regular", "parallelism=2"); -- Schedule an SWCS compaction with default time window and parallelism set to 2 -- admin compact_table("test", "swcs", "parallelism=2"); -- Schedule an SWCS compaction with custom time window and parallelism -- admin compact_table("test", "swcs", "window=1800,parallelism=2"); -- Schedule a regular compaction for the half-open time range [start_time, end_time) -- admin compact_table("test", "regular", "start_time=2026-01-01T00:00:00Z,end_time=2026-02-01T00:00:00Z"); -- Schedule an SWCS compaction for a time range -- admin compact_table("test", "strict_window", "window=3600,start_time=2026-01-01T00:00:00Z,end_time=2026-02-01T00:00:00Z"); -- Build missing indexes for existing SST files after adding or changing indexes -- admin build_index("test"); -- Garbage collect orphaned SST files for a dropped table -- admin gc_table("test"); -- Garbage collect orphaned SST files for a dropped table with full file listing -- admin gc_table("test", true); -- Garbage collect orphaned SST files for specific regions -- admin gc_regions(1, 2, 3); -- Garbage collect orphaned SST files for specific regions with full file listing -- admin gc_regions(1, 2, 3, true); -- Permanently purge a soft-dropped table -- admin purge_table("test"); ``` ## Discard Unflushed Data Use `admin discard_unflushed` as an emergency recovery operation when data in a Memtable repeatedly prevents flushing, fills the write buffer, and blocks further writes. :::danger This operation permanently deletes all data in the target regions that has not been persisted to SST files. It also makes the corresponding WAL entries obsolete, so restarting a Datanode does not restore the discarded data. Data already persisted in SST files remains available. ::: The function takes exactly one table name or Region ID: ```sql ADMIN discard_unflushed('table_name'); ADMIN discard_unflushed(region_id); ``` A table name targets all physical regions of the table. It can be unqualified, schema-qualified, or fully qualified; omitted qualifiers are resolved from the current query context. A numeric Region ID targets only that Region. Query the [`information_schema.PARTITIONS`](./information-schema/partitions.md) table to find Region IDs. Metric Engine logical tables are not supported because multiple logical tables share the same physical regions. Using a physical Metric Engine table name or physical Region ID discards unflushed data shared by its logical tables, so verify the target carefully. This function is available only through the `ADMIN` statement. Calling it in a `SELECT` statement is rejected. Repeating the same operation when no unflushed data remains is safe and has no effect. ## Build Index Use `admin build_index` to manually build indexes for existing data files when the table metadata requires indexes that some SST files do not have yet. Typical use cases include adding an index to an existing column, migrating from data written before the index was available, or retrying after a previous index build failure. ```sql admin build_index("table_name"); ``` The function takes exactly one string argument. The table name can be unqualified or fully qualified. Unqualified names are resolved with the current query context. For example, build a fulltext index for existing data: ```sql CREATE TABLE logs ( ts TIMESTAMP TIME INDEX, message TEXT ); INSERT INTO logs VALUES (1, 'The quick brown fox jumps over the lazy dog'), (2, 'The quick brown fox jumps over the lazy cat'); admin flush_table("logs"); ALTER TABLE logs MODIFY COLUMN message SET FULLTEXT INDEX; admin build_index("logs"); SELECT message FROM logs WHERE matches_term(message, 'fox'); ``` `admin build_index` sends build requests to all regions of the table. Each region only builds indexes for SST files whose recorded index metadata is inconsistent with the current table metadata. Files that already have the required index metadata are skipped, so rerunning the command is safe. The command currently returns an affected-row count. Use `SHOW INDEX` to check logical index definitions: ```sql SHOW INDEX FROM logs; ``` You can also query `information_schema.ssts_index_meta` to check physical index metadata for SST files: ```sql SELECT COUNT(*) AS fulltext_index_meta_count FROM information_schema.ssts_index_meta WHERE table_id = ( SELECT table_id FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'logs' ) AND index_type LIKE 'fulltext%'; ``` Building indexes reads SST data and writes index files, so it consumes CPU, memory, and I/O resources. In asynchronous index build mode, automatic flush, compaction, and schema-change triggers may run at the same time as a manual build. Duplicate in-flight work is deduplicated or aborted, and the command remains safe to rerun. --- ## ALTER `ALTER` can be used to modify any database options, table options or metadata of the table, including: - Modify database options - Add/Drop/Modify a column - Set/Drop column default values - Drop column default values - Rename a table - Modify table options ## ALTER DATABASE `ALTER DATABASE` statements can be used to modify the options of databases. ### Syntax ```sql ALTER DATABASE db [SET = [, ...] | UNSET [, ...] ] ``` Currently following options are supported: - `ttl`: Specifies the default retention time for data in the database. Data exceeding this retention period will be deleted asynchronously. - If `ttl` was not previously set, defining a new `ttl` using `ALTER` will result in the deletion of data that exceeds the specified retention time. - If `ttl` was already set, modifying it via `ALTER` will enforce the updated retention time immediately, removing data that exceeds the new retention threshold. - If `ttl` was previously set and is unset using `ALTER`, new data will no longer be deleted. However, data that was previously deleted due to the retention policy cannot be restored. - `compaction.twcs.time_window`: the time window parameter of TWCS compaction strategy. The value should be a [time duration string](/reference/time-durations.md). Changes to this option will immediately affect all tables that don't have their own explicit compaction settings. - `compaction.twcs.max_output_file_size`: the maximum allowed output file size of TWCS compaction strategy. Changes to this option will immediately affect all tables that don't have their own explicit compaction settings. - `compaction.twcs.trigger_file_num`: the number of files in a specific time window to trigger a compaction. Changes to this option will immediately affect all tables that don't have their own explicit compaction settings. ### Examples #### Modify default retention time of data in database Change the default retention time of data in the database to 1 day: ```sql ALTER DATABASE db SET 'ttl'='1d'; ``` Remove the default retention time of data in the database: ```sql ALTER DATABASE db UNSET 'ttl'; ``` #### Modify compaction options of database Database-level compaction options are dynamically resolved at compaction scheduling time. When you modify these options, the changes will immediately affect all tables in the database that don't have their own explicit compaction settings, similar to how TTL works. Change the compaction time window for the database: ```sql ALTER DATABASE db SET 'compaction.twcs.time_window'='2h'; ``` Change the maximum output file size for compaction: ```sql ALTER DATABASE db SET 'compaction.twcs.max_output_file_size'='500MB'; ``` Change the trigger file number for compaction: ```sql ALTER DATABASE db SET 'compaction.twcs.trigger_file_num'='8'; ``` Remove compaction options: ```sql ALTER DATABASE db UNSET 'compaction.twcs.time_window'; ``` ## ALTER TABLE ### Syntax ```sql ALTER TABLE [db.]table [ADD COLUMN name1 type1 [options], ADD COLUMN name2 type2 [options], ... | DROP COLUMN name | MODIFY COLUMN name type | MODIFY COLUMN name SET DEFAULT value | MODIFY COLUMN name DROP DEFAULT | MODIFY COLUMN name SET FULLTEXT INDEX [WITH ] | MODIFY COLUMN name UNSET FULLTEXT INDEX | PARTITION ON COLUMNS () () | SPLIT PARTITION () [ON COLUMNS ()] INTO () | MERGE PARTITION () | RENAME name | SET = [, ...] | UNSET [, ...] ] ``` ### Add column Adds a new column to the table: ```sql ALTER TABLE monitor ADD COLUMN load_15 double; ``` Definition of column is the same as in [CREATE](./create.md). Also, we can add multiple columns to the table at the same time: ```sql ALTER TABLE monitor ADD COLUMN disk_usage double, ADD COLUMN disk_free double; ``` We can set the new column's location. In first position for example: ```sql ALTER TABLE monitor ADD COLUMN load_15 double FIRST; ``` After an existing column: ```sql ALTER TABLE monitor ADD COLUMN load_15 double AFTER memory; ``` Adds a new column as a tag(primary key) with a default value: ```sql ALTER TABLE monitor ADD COLUMN app STRING DEFAULT 'shop' PRIMARY KEY; ``` ### Remove column Removes a column from the table: ```sql ALTER TABLE monitor DROP COLUMN load_15; ``` The removed column can't be retrieved immediately by all subsequent queries. ### Modify column type Modify the date type of a column ```sql ALTER TABLE monitor MODIFY COLUMN load_15 STRING; ``` The modified column cannot be a tag (primary key) or time index, and it must be nullable to ensure that the data can be safely converted (returns `NULL` on cast failures). ### Set column default value Set a default value for an existing column: ```sql ALTER TABLE monitor MODIFY COLUMN load_15 SET DEFAULT 0.0; ``` Set a string default value: ```sql ALTER TABLE monitor MODIFY COLUMN `status` SET DEFAULT 'active'; ``` The default value will be used for new rows when no explicit value is provided for the column during insertion. Remove the default value from a column: ```sql ALTER TABLE monitor MODIFY COLUMN load_15 DROP DEFAULT; ``` After dropping the default value, the column will use `NULL` as the default. The database only allow dropping defaults for nullable columns. ### Alter table options `ALTER TABLE` statements can also be used to change the options of tables. Currently following options are supported: - `ttl`: the retention time of data in table. - `append_mode`: whether the table is append-only. You can change it from `false` to `true`, but not from `true` to `false`. - `compaction.twcs.time_window`: the time window parameter of TWCS compaction strategy. The value should be a [time duration string](/reference/time-durations.md). - `compaction.twcs.max_output_file_size`: the maximum allowed output file size of TWCS compaction strategy. - `compaction.twcs.trigger_file_num`: the number of files in a specific time window to trigger a compaction. - `sst_format`: the SST format of the table. The value can be `flat` or `primary_key`. A table supports changing the format in both directions: `primary_key` to `flat` and `flat` to `primary_key`. - `write_buffer_size`: the per-region write buffer stall threshold of the table. For a positive value such as `512MB`, GreptimeDB schedules a flush when mutable memtable usage reaches half the value, stalls writes at the value, and rejects writes at twice the value. The table option overrides `region_engine.mito.default_region_write_buffer_size`. Setting it to `0` explicitly disables the per-region limit even when the engine default is nonzero. Unsetting it removes the table override and falls back to the engine default. - `auto_flush_interval`: how long a region of this table may go without a flush before one is triggered. The value is a [time duration string](/reference/time-durations.md) and must be greater than zero. The table option overrides the engine-wide `region_engine.mito.auto_flush_interval`. - `skip_wal`: whether to disable Write-Ahead-Log for this table. When set to `'true'`, the data written to the table will not be persisted to the write-ahead log, which can improve write throughput. However, when the process restarts, any unflushed data will be lost. Please use this feature only when the data source itself can ensure reliability. You can change it from `false` to `true`, but you cannot change it back to `false` or unset it. Enabling it is only supported on physical tables using the `mito` or `metric` engine, and it must be set on its own: combining it with other table options in the same `ALTER TABLE` is rejected. - `max_row_group_row_count`: the maximum number of rows in a Parquet row group. The value must be from `1` through `10485760` (`10 * 1024 * 1024`); zero is rejected. Changing or unsetting this option flushes pending rows using the old row group size before applying the new value. The new value, or the default of `102400` (`100 * 1024`) after unsetting, applies to subsequently produced SSTs. The ALTER operation does not immediately rewrite existing SSTs; later compactions may rewrite them using the current row group size. ```sql ALTER TABLE monitor SET 'ttl'='1d'; ALTER TABLE monitor SET 'append_mode'='true'; ALTER TABLE monitor SET 'compaction.twcs.time_window'='2h'; ALTER TABLE monitor SET 'compaction.twcs.max_output_file_size'='500MB'; ALTER TABLE monitor SET 'compaction.twcs.trigger_file_num'='8'; ALTER TABLE monitor SET 'sst_format'='flat'; ALTER TABLE monitor SET 'sst_format'='primary_key'; ALTER TABLE monitor SET 'write_buffer_size'='512MB'; ALTER TABLE monitor SET 'auto_flush_interval'='5m'; ALTER TABLE monitor SET 'skip_wal'='true'; ALTER TABLE monitor SET 'max_row_group_row_count'='2048'; ``` To drop the `auto_flush_interval` override and fall back to the engine-wide setting, set it to `NULL`: ```sql ALTER TABLE monitor SET 'auto_flush_interval' = NULL; ``` :::warning `auto_flush_interval` does not support `UNSET`. `ALTER TABLE monitor UNSET 'auto_flush_interval'` returns an error; use `SET ... = NULL` as shown above. ::: ### Unset table options ```sql ALTER TABLE monitor UNSET 'ttl'; ALTER TABLE monitor UNSET 'write_buffer_size'; ALTER TABLE monitor UNSET 'max_row_group_row_count'; ``` ### Set repartition column hint :::info Enterprise feature This option is available in GreptimeDB Enterprise. For details, see [Auto Repartition](/enterprise/autopilot/auto-repartition.md). ::: In GreptimeDB Enterprise, `repartition.column.hint` can be specified in `CREATE TABLE ... WITH` or changed later with `ALTER TABLE`. For an unpartitioned table, you can set the preferred column used by Auto Repartition: ```sql ALTER TABLE table_name SET 'repartition.column.hint'='column_name'; ``` To remove the hint: ```sql ALTER TABLE table_name UNSET 'repartition.column.hint'; ``` The hint only records metadata for future Auto Repartition. It does not trigger Repartition immediately. When using `ALTER TABLE`, the hint must be set or unset separately from other table options. ### Repartition, split, or merge partitions Use `PARTITION ON COLUMNS` to repartition an unpartitioned table into multiple partitions: ```sql ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id, area) ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South', device_id >= 100 AND area <= 'East', device_id >= 100 AND area > 'East' ); ``` Use `SPLIT PARTITION` to split an existing partition into multiple partitions: ```sql ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 ) INTO ( device_id < 100 AND area < 'North', device_id < 100 AND area >= 'North' ); ``` To also introduce a new partition column during a split, add `ON COLUMNS`. In the following example, the table is currently partitioned only by `device_id`, and `ON COLUMNS (device_id, area)` introduces `area` as a new partition column: ```sql ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 ) ON COLUMNS (device_id, area) INTO ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ); ``` When `ON COLUMNS` is omitted, the existing partition columns are preserved. Use `MERGE PARTITION` to merge multiple partitions into one. The following example merges two partitions into a single partition covering `device_id < 100`: ```sql ALTER TABLE sensor_readings MERGE PARTITION ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ); ``` You can provide DDL options after any repartition statement to control execution behavior: ```sql ALTER TABLE sensor_readings SPLIT PARTITION ( device_id < 100 ) INTO ( device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South' ) WITH ( TIMEOUT = '5m', WAIT = false ); ``` When `WAIT = false`, the statement returns a `procedure_id`. You can check the status with `ADMIN procedure_state(procedure_id)` (see [ADMIN](/reference/sql/admin.md)). `TIMEOUT` controls the overall time limit for the operation, and it is enforced regardless of whether `WAIT` is `true` or `false`. :::caution Note Repartitioning operations are only supported in distributed clusters. You must enable shared object storage and GC, and ensure all datanodes can access the same object store before running these statements. ::: ### Create an index for a column Enable inverted index on a column: ```sql ALTER TABLE monitor MODIFY COLUMN host SET INVERTED INDEX; ``` Enable fulltext index on a column: ```sql ALTER TABLE monitor MODIFY COLUMN load_15 SET FULLTEXT INDEX WITH (analyzer = 'Chinese', case_sensitive = 'false', backend = 'bloom', granularity = 1024, false_positive_rate = 0.01); ``` You can specify the following options using `FULLTEXT INDEX WITH` when enabling fulltext options: - `analyzer`: Sets the language analyzer for the full-text index. Supported values are `English` and `Chinese`. Default is `English`. - `case_sensitive`: Determines whether the full-text index is case-sensitive. Supported values are `true` and `false`. Default is `false`. - `backend`: Sets the backend for the full-text index. Supported values are `bloom` and `tantivy`. Default is `bloom`. - `granularity`: (For `bloom` backend) The size of data chunks covered by each filter. A smaller granularity improves filtering but increases index size. Default is `10240`. - `false_positive_rate`: (For `bloom` backend) The probability of misidentifying a block. A lower rate improves accuracy (better filtering) but increases index size. Value is a float between `0` and `1`. Default is `0.01`. For more information on full-text index configuration and performance comparison, refer to the [Full-Text Index Configuration Guide](/user-guide/manage-data/data-index.md#full-text-index). If `WITH ` is not specified, `FULLTEXT INDEX` will use the default values. Enable skipping index on a column: ```sql ALTER TABLE monitor MODIFY COLUMN host SET SKIPPING INDEX WITH(granularity = 1024, type = 'BLOOM', false_positive_rate = 0.01); ``` The valid options for the skipping index include: - `type`: The index type, only supports `BLOOM` type right now. - `granularity`: (For `BLOOM` type) The size of data chunks covered by each filter. A smaller granularity improves filtering but increases index size. Default is `10240`. - `false_positive_rate`: (For `BLOOM` type) The probability of misidentifying a block. A lower rate improves accuracy (better filtering) but increases index size. Value is a float between `0` and `1`. Default is `0.01`. ### Remove index on a column The syntax is: ```sql ALTER TABLE [table] MODIFY COLUMN [column] UNSET [INVERTED | SKIPPING | FULLTEXT] INDEX; ``` For example, remove the inverted index: ```sql ALTER TABLE monitor_pk MODIFY COLUMN host UNSET INVERTED INDEX; ``` Remove the skipping index: ```sql ALTER TABLE monitor_pk MODIFY COLUMN host UNSET SKIPPING INDEX; ``` Remove the fulltext index: ```sql ALTER TABLE monitor MODIFY COLUMN load_15 UNSET FULLTEXT INDEX; ``` The column must be a string type to alter the fulltext index. If the fulltext index has never been enabled, you can enable it and specify the `analyzer` and `case_sensitive` options. When the fulltext index is already enabled on a column, you can disable it but **cannot modify the options, and so does the skipping index**. ### Rename table Renames the table: ```sql ALTER TABLE monitor RENAME monitor_new; ``` This command only renames the table; it doesn't modify the data within the table. --- ## CASE The `CASE` expression evaluates conditions in order and returns the result for the first condition that is true. If no condition is true, it returns the `ELSE` result, or `NULL` when `ELSE` is omitted. ## Syntax ```sql CASE WHEN condition1 THEN result1 WHEN condition2 THEN result2 ... ELSE result END ``` - `condition1`, `condition2`, ...: The conditions to evaluate against the expression. - `result1`, `result2`, ...: The values to return when the corresponding condition is met. - `result`: The value to return when none of the conditions are met (optional). ## Examples The `CASE` statement can be used in various clauses, such as `SELECT`, `WHERE`, `ORDER BY` and `GROUP BY`. ### Use `CASE` in `SELECT` In the `SELECT` clause, you can use the `CASE` statement to create new columns based on conditions. please see [the example](/user-guide/query-data/sql.md#case-expression) in the query data guide. You can also use `CASE` with functions like `SUM` to conditionally aggregate data. for example, you can calculate the total number of logs with status 200 and 404: ```sql SELECT SUM(CASE WHEN status_code = '200' THEN 1 ELSE 0 END) AS status_200_count, SUM(CASE WHEN status_code = '404' THEN 1 ELSE 0 END) AS status_404_count FROM nginx_logs; ``` ### Use `CASE` in `WHERE` In the `WHERE` clause, you can filter rows based on conditions. For example, the following query retrieves data from the `monitor` table based on the `ts` condition: ```sql SELECT * FROM monitor WHERE host = CASE WHEN ts > '2023-12-13 02:05:46' THEN '127.0.0.1' ELSE '127.0.0.2' END; ``` ### Use `CASE` in `GROUP BY` The `CASE` statement can be utilized in the `GROUP BY` clause to categorize data based on specific conditions. For instance, the following query groups data by the `host` column and classifies the `cpu` column into three categories: 'high', 'medium', and 'low': ```sql SELECT host, COUNT(*) AS count, CASE WHEN cpu > 0.5 THEN 'high' WHEN cpu > 0.3 THEN 'medium' ELSE 'low' END AS cpu_status FROM monitor GROUP BY host, cpu_status; ``` ### Use `CASE` in `ORDER BY` The following query sorts rows by `status_code` when it is not `NULL`, and by `http_method` otherwise: ```sql SELECT * FROM nginx_logs ORDER BY CASE WHEN status_code IS NOT NULL THEN status_code ELSE http_method END; ``` --- ## CAST `CAST` is used to convert an expression of one data type to another. ## Syntax ```sql CAST (expression AS data_type) ``` ## Parameters - expression: the expression to be converted. - data_type: the data type to convert the expression to. # Examples Convert a string to an integer: ```sql SELECT CAST('123' AS INT) ; ``` ```sql +-------------+ | Utf8("123") | +-------------+ | 123 | +-------------+ ``` --- ## COMMENT The `COMMENT` statement is used to add or remove comments on tables, columns, and flows. Comments provide descriptions that can help document the purpose and usage of database objects. ## COMMENT ON TABLE `COMMENT ON TABLE` adds or removes a comment on a table. ### Syntax ```sql COMMENT ON TABLE table_name IS { 'comment' | NULL } ``` - `table_name`: The name of the table to comment on. - `'comment'`: A string literal containing the comment text. - `NULL`: Removes the existing comment from the table. ### Examples Add a comment to a table: ```sql COMMENT ON TABLE system_metrics IS 'System monitoring metrics collected every minute'; ``` Remove a comment from a table: ```sql COMMENT ON TABLE system_metrics IS NULL; ``` View the table comment using `SHOW CREATE TABLE`: ```sql SHOW CREATE TABLE system_metrics; ``` The comment can also be viewed through the `INFORMATION_SCHEMA.TABLES` table by querying the `table_comment` column. ## COMMENT ON COLUMN `COMMENT ON COLUMN` adds or removes a comment on a specific column of a table. ### Syntax ```sql COMMENT ON COLUMN table_name.column_name IS { 'comment' | NULL } ``` - `table_name`: The name of the table containing the column. - `column_name`: The name of the column to comment on. - `'comment'`: A string literal containing the comment text. - `NULL`: Removes the existing comment from the column. ### Examples Add a comment to a column: ```sql COMMENT ON COLUMN system_metrics.cpu_usage IS 'CPU usage percentage (0-100)'; ``` Add comments to multiple columns: ```sql COMMENT ON COLUMN system_metrics.memory_usage IS 'Memory usage in bytes'; COMMENT ON COLUMN system_metrics.disk_usage IS 'Disk usage percentage'; ``` Remove a comment from a column: ```sql COMMENT ON COLUMN system_metrics.cpu_usage IS NULL; ``` View column comments using `SHOW CREATE TABLE`: ```sql SHOW CREATE TABLE system_metrics; ``` Column comments can also be queried from the `INFORMATION_SCHEMA.COLUMNS` table by accessing the `column_comment` column. ## COMMENT ON FLOW `COMMENT ON FLOW` adds or removes a comment on a flow. ### Syntax ```sql COMMENT ON FLOW flow_name IS { 'comment' | NULL } ``` - `flow_name`: The name of the flow to comment on. - `'comment'`: A string literal containing the comment text. - `NULL`: Removes the existing comment from the flow. ### Examples Add a comment to a flow: ```sql COMMENT ON FLOW temperature_monitoring IS 'Monitors temperature sensors and alerts on high values'; ``` Remove a comment from a flow: ```sql COMMENT ON FLOW temperature_monitoring IS NULL; ``` View the flow comment using `SHOW CREATE FLOW`: ```sql SHOW CREATE FLOW temperature_monitoring; ``` Flow comments can also be queried from the `INFORMATION_SCHEMA.FLOWS` table by accessing the `comment` column. ## Notes - Comments are stored as metadata and do not affect the behavior or performance of tables, columns, or flows. - Comments can be updated by issuing a new `COMMENT ON` statement with a different comment text. - Setting a comment to `NULL` removes the existing comment but does not generate an error if no comment exists. - Comments are particularly useful for documenting the purpose of database objects, especially in collaborative environments. --- ## ANSI Compatibility GreptimeDB supports a subset of ANSI SQL and has some unique extensions. Some major incompatibilities and extensions are described below: 1. Create a table: * Supports the unique `TIME INDEX` constraint. Please refer to the [Data Model](/user-guide/concepts/data-model.md) and the [CREATE](./create.md) table creation syntax for details. * Currently only supports `PRIMARY KEY` constraints and does not support other types of constraints or foreign keys. * GreptimeDB is a native distributed database, so the table creation syntax for distributed tables supports partitioning rules. Please also refer to the [CREATE](./create.md). 2. Insert data: Consistent with ANSI SQL syntax, but requires the `TIME INDEX` column value (or default value) to be provided. 3. Update data: Does not support `UPDATE` syntax, but if the primary key and `TIME INDEX` corresponding column values are the same during `INSERT`, subsequent inserted rows will overwrite previously written rows, effectively achieving an update. * Since 0.8, GreptimeDB supports [append mode](/reference/sql/create.md#create-an-append-only-table) that creates an append-only table with `append_mode="true"` option which keeps duplicate rows. * GreptimeDB supports [merge mode](/reference/sql/create.md#create-a-table-with-merge-mode) that creates a table with `merge_mode="last_non_null"` option which allow updating a field partially. 4. Query data: Query syntax is compatible with ANSI SQL, with some functional differences and omissions. * Since v0.9.0, begins to support [VIEW](/user-guide/query-data/view.md). * TQL syntax extension: Supports executing PromQL in SQL via TQL subcommands. Please refer to the [TQL](./tql.md) section for details. * [Range Query](/reference/sql/range.md) to query and aggregate data within a range of time. 5. Delete data: Deletion syntax is basically consistent with ANSI SQL. 6. Others: * Identifiers such as table names and column names have constraints similar to ANSI SQL, are case sensitive, and require double quotes when encountering special characters or uppercase letters. * GreptimeDB has optimized identifier rules for different dialects. For example, when you connect with a MySQL or PostgreSQL client, you can use identifier rules specific to that SQL dialect, such as using backticks `` ` `` for MySQL and standard double quotes `"` for PostgreSQL. --- ## COPY ## COPY TABLE ### COPY TO `COPY TO` is used to export the contents of a table to a file. The syntax for using `COPY TO` is as follows: ```sql COPY tbl TO '/xxx/xxx/output.parquet' WITH (FORMAT = 'parquet'); ``` The command starts with the keyword `COPY`, followed by the name of the table you want to export data from (`tbl` in this case). `TO` specifies the file path and name to save the exported data (`/xxx/xxx/output.parquet` in this case). :::tip NOTE The output file is created on the GreptimeDB server node that executes the query, not on the client machine that issues the SQL. Make sure the path is accessible and writable on that server, or export to S3, GCS, Azure Blob Storage, or other supported cloud storage services via `CONNECTION`. In standalone deployments, local file paths are sandboxed to the `storage.copy_root` directory (default `/copy`). In distributed deployments, local file access is disabled entirely. See [Migrate Local SQL File Access](/user-guide/deployments-administration/migrate-local-sql-file-access.md) for details. ::: For example, to export data to CSV or JSON with custom timestamp and date formats: ```sql COPY tbl TO '/path/to/file.csv' WITH ( FORMAT = 'csv', TIMESTAMP_FORMAT = '%Y/%m/%d %H:%M:%S', DATE_FORMAT = '%Y-%m-%d' ); ``` ```sql COPY tbl TO '/path/to/file.json' WITH ( FORMAT = 'json', TIMESTAMP_FORMAT = '%Y/%m/%d %H:%M:%S', DATE_FORMAT = '%Y-%m-%d' ); ``` You can also export data to a compressed CSV or JSON file: ```sql COPY tbl TO '/path/to/file.csv.gz' WITH ( FORMAT = 'csv', COMPRESSION_TYPE = 'gzip' ); ``` :::tip NOTE When using compression, ensure the file extension matches the compression type: `.gz` for gzip, `.zst` for zstd, `.bz2` for bzip2, and `.xz` for xz. ::: #### `WITH` Option `WITH` adds options such as the file `FORMAT` which specifies the format of the exported file. In this example, the format is Parquet; it is a columnar storage format used for big data processing. Parquet efficiently compresses and encodes columnar data for big data analytics. | Option | Description | Required | |---|---|---| | `FORMAT` | Target file(s) format, e.g., JSON, CSV, Parquet | **Required** | | `START_TIME`/`END_TIME`| The time range within which data should be exported. `START_TIME` is inclusive and `END_TIME` is exclusive. | Optional | | `COMPRESSION_TYPE` | Compression algorithm for the exported file. Supported values: `gzip`, `zstd`, `bzip2`, `xz`. Only supported for CSV and JSON formats. | Optional | | `TIMESTAMP_FORMAT` | Custom format for timestamp columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers (e.g., `'%Y-%m-%d %H:%M:%S'`). Supported for CSV and JSON formats. | Optional | | `DATE_FORMAT` | Custom format for date columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers (e.g., `'%Y-%m-%d'`). Supported for CSV and JSON formats. | Optional | | `TIME_FORMAT` | Custom format for time columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers (e.g., `'%H:%M:%S'`). Supported for CSV and JSON formats. | Optional | #### `CONNECTION` Option `COPY TO` supports exporting data to cloud storage services. See [connect-to-s3](#connect-to-s3), [connect-to-gcs](#connect-to-gcs), or [connect-to-azure-blob-storage](#connect-to-azure-blob-storage) for more details. ### COPY FROM `COPY FROM` is used to import data from a file into a table. The syntax for using `COPY FROM` is as follows: ```sql COPY [.] FROM { '/[]' } [ [ WITH ] ( [ FORMAT = { 'CSV' | 'JSON' | 'PARQUET' | 'ORC' } ] [ PATTERN = '' ] ) ] [LIMIT NUM] ``` The command starts with the keyword `COPY`, followed by the name of the table you want to import data into. #### `WITH` Option `FORMAT` specifies the file format of the imported file. In this example, the format is Parquet. The option `PATTERN` allows the usage of wildcard characters like * to specify multiple input files that match a certain pattern. For example, you can use the following syntax to import all files in the directory(which must be an absolute path) "/path/to/folder" with the filename that contains `parquet`: ```sql COPY tbl FROM '/path/to/folder/' WITH (FORMAT = 'parquet', PATTERN = '.*parquet.*'); ``` Specifically, if you only have one file to import, you can use the following syntax: ```sql COPY tbl FROM '/path/to/folder/xxx.parquet' WITH (FORMAT = 'parquet'); ``` You can also import data from a compressed CSV or JSON file: ```sql COPY tbl FROM '/path/to/file.csv.gz' WITH ( FORMAT = 'csv', COMPRESSION_TYPE = 'gzip' ); ``` When importing CSV data, you can set `SKIP_BAD_RECORDS = 'true'` to skip rows with parsing or casting errors and continue importing: ```sql COPY tbl FROM '/path/to/file.csv' WITH ( FORMAT = 'csv', SKIP_BAD_RECORDS = 'true' ); ``` | Option | Description | Required | |---|---|---| | `FORMAT` | Target file(s) format, e.g., JSON, CSV, Parquet, ORC | **Required** | | `PATTERN` | Use regex to match files. e.g., `*_today.parquet` | Optional | | `COMPRESSION_TYPE` | Compression algorithm for the imported file. Supported values: `gzip`, `zstd`, `bzip2`, `xz`. Only supported for CSV and JSON formats. | Optional | | `HEADERS` | Whether the CSV file has a header row. Supported values: `true`, `false`. Default is `true`. | Optional | | `SKIP_BAD_RECORDS` | Whether to skip rows with parsing or casting errors during CSV import. Supported values: `true`, `false`. Default is `false`. Only supported for CSV format. | Optional | | `STRICT_HEADERS` | Whether to validate CSV headers against the table schema before importing. Supported values: `true`, `false`. Default is `false`. Only supported for CSV format, and requires `HEADERS = 'true'`. | Optional | :::tip NOTE By default, CSV files are parsed with headers and columns are matched by name. To import headerless CSV files, set `HEADERS = 'false'`. In this mode, columns are mapped by position following the target table schema order. You can use `SHOW CREATE TABLE ` to inspect the table column order. ::: #### STRICT_HEADERS Option By default, header matching is lenient: CSV columns the table does not have are ignored, and table columns the CSV does not have fall back to the normal insert and default-value handling. Set `STRICT_HEADERS = 'true'` to validate the header row before any data is read. The import fails if the CSV has: - a column the table does not have, - no column for a column the table does have, - the same header name more than once. ```sql COPY monitor FROM 'monitor.csv' WITH (FORMAT = 'CSV', STRICT_HEADERS = 'true'); ``` A mismatch reports all three categories at once: ``` ERROR: CSV header mismatch in path: monitor.csv, unknown columns: ["extra"], missing columns: [], duplicate columns: [] ``` :::warning The missing-column check covers **every** column in the table, including nullable columns and columns with a default value. A CSV that omits any of them is rejected. `STRICT_HEADERS = 'true'` cannot be combined with `HEADERS = 'false'` — a headerless CSV has no header names to validate. The combination returns `strict_headers=true requires headers=true`. ::: #### `CONNECTION` Option `COPY FROM` also supports importing data from cloud storage services. See [connect-to-s3](#connect-to-s3), [connect-to-gcs](#connect-to-gcs), or [connect-to-azure-blob-storage](#connect-to-azure-blob-storage) for more details. ### Connect to S3 You can copy data from/to S3 ```sql -- COPY FROM COPY tbl FROM '' WITH (FORMAT = 'parquet') CONNECTION(REGION = 'us-west-2'); -- COPY TO COPY tbl TO '' WITH (FORMAT = 'parquet') CONNECTION(REGION = 'us-west-2'); ``` #### URL Notes: You should specify a file using `S3://bucket/key-name`. The following example shows the correct format. ``` s3://my-bucket/data.parquet ``` Another way is using Virtual-hosted–style(`ENABLE_VIRTUAL_HOST_STYLE` must be set to `true` to enable this). The following example shows the correct format. ``` s3://bucket-name.s3.region-code.amazonaws.com/key-name ``` :::tip NOTE You can use `Copy S3 URI` or `COPY URL` on S3 console to get S3 URI/HTTP URL prefix or full path. ::: #### Options You can set the following **CONNECTION** options: | Option | Description | Required | |---|---|---| | `REGION` | AWS region name. e.g., `us-west-2` | **Required** | | `ENDPOINT` | The bucket endpoint. e.g., `s3.us-west-2.amazonaws.com` | Optional | | `ACCESS_KEY_ID` | ACCESS_KEY_ID Your access key ID for connecting the AWS S3 compatible object storage. | Optional | | `SECRET_ACCESS_KEY` | Your secret access key for connecting the AWS S3 compatible object storage. | Optional | | `ENABLE_VIRTUAL_HOST_STYLE` | If you use virtual hosting to address the bucket, set it to "true".| Optional | | `SESSION_TOKEN` | Your temporary credential for connecting the AWS S3 service. | Optional | #### LIMIT You can use `LIMIT` to restrict maximum number of rows inserted at once. ### Connect to GCS You can copy data from/to Google Cloud Storage (GCS). ```sql -- COPY FROM COPY tbl FROM 'gcs:///' WITH (FORMAT = 'parquet') CONNECTION(SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write'); -- COPY TO COPY tbl TO 'gcs:///' WITH (FORMAT = 'parquet') CONNECTION(SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write'); ``` #### URL Specify the file or directory using `gcs://bucket/path`. For example: ``` gcs://my-bucket/data.parquet ``` #### Options | Option | Description | Required | |---|---|---| | `SCOPE` | The scope for GCS access, e.g., `https://www.googleapis.com/auth/devstorage.read_write` | Optional | | `CREDENTIAL` | The service account credential content in JSON format. | Optional | | `ENDPOINT` | The endpoint of the GCS service. | Optional | ### Connect to Azure Blob Storage You can copy data from/to Azure Blob Storage. ```sql -- COPY FROM COPY tbl FROM 'azblob:///' WITH (FORMAT = 'parquet') CONNECTION(ACCOUNT_NAME = 'my-account', ACCOUNT_KEY = 'my-key'); -- COPY TO COPY tbl TO 'azblob:///' WITH (FORMAT = 'parquet') CONNECTION(ACCOUNT_NAME = 'my-account', ACCOUNT_KEY = 'my-key'); ``` #### URL Specify the file or directory using `azblob://container/path`. For example: ``` azblob://my-container/data.parquet ``` #### Options | Option | Description | Required | |---|---|---| | `ACCOUNT_NAME` | Azure storage account name. | Optional | | `ACCOUNT_KEY` | Azure storage account key. | Optional | | `ENDPOINT` | The endpoint of the Azure Blob Storage service. | Optional | | `SAS_TOKEN` | Shared access signature (SAS) token for Azure Blob Storage. | Optional | ## COPY Query Results You can use the `COPY` statement to export the results of a query to a file. The syntax is as follows: ```sql COPY () TO '' WITH (FORMAT = { 'CSV' | 'JSON' | 'PARQUET' }); ``` | Option | Description | Required | |---|---|---| | `QUERY` | The SQL SELECT statement to execute | **Required** | | `PATH` | The file path where the output will be written | **Required** | | `FORMAT` | The output file format: 'CSV', 'JSON', or 'PARQUET' | **Required** | | `COMPRESSION_TYPE` | Compression algorithm for the exported file. Supported values: `gzip`, `zstd`, `bzip2`, `xz`. Only supported for CSV and JSON formats. | Optional | | `TIMESTAMP_FORMAT` | Custom format for timestamp columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers. Supported for CSV and JSON formats. | Optional | | `DATE_FORMAT` | Custom format for date columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers. Supported for CSV and JSON formats. | Optional | | `TIME_FORMAT` | Custom format for time columns when exporting to CSV or JSON format. Uses [strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) format specifiers. Supported for CSV and JSON formats. | Optional | ### PostgreSQL `COPY ... TO STDOUT` When you connect through the PostgreSQL protocol, you can stream query results to the client with `COPY () TO STDOUT`. This is useful when you want the output to be handled by the PostgreSQL client, such as `psql`, instead of writing a file on the GreptimeDB server. PostgreSQL-compatible option syntax is supported in both forms: `WITH (...)` and bare `(...)`. ```sql COPY (SELECT * FROM tbl WHERE host = 'host1') TO STDOUT; COPY (SELECT * FROM tbl WHERE host = 'host1') TO STDOUT WITH (FORMAT csv); COPY (SELECT * FROM tbl WHERE host = 'host1') TO STDOUT (FORMAT binary); ``` - `FORMAT` supports PostgreSQL text output (default when omitted), `csv`, and `binary`. - `COPY ... TO STDOUT` is only supported for query results, that is, `COPY ()`, on the PostgreSQL protocol. For example, in `psql` you can use `\copy` to save the streamed result to a local file on the client machine: ```sql \copy (SELECT * FROM tbl WHERE host = 'host1') TO '/tmp/file.csv' WITH (FORMAT csv) ``` For example, the following statement exports query results to a CSV file: ```sql COPY (SELECT * FROM tbl WHERE host = 'host1') TO '/path/to/file.csv' WITH (FORMAT = 'csv'); ``` You can also export query results to a compressed file: ```sql COPY (SELECT * FROM tbl WHERE host = 'host1') TO '/path/to/file.json.gz' WITH ( FORMAT = 'json', COMPRESSION_TYPE = 'gzip' ); ``` You can also specify custom date and time formats when exporting to CSV or JSON: ```sql COPY (SELECT * FROM tbl WHERE host = 'host1') TO '/path/to/file.csv' WITH ( FORMAT = 'csv', TIMESTAMP_FORMAT = '%m-%d-%Y %H:%M:%S', DATE_FORMAT = '%Y/%m/%d' ); ``` ```sql COPY (SELECT * FROM tbl WHERE host = 'host1') TO '/path/to/file.json' WITH ( FORMAT = 'json', TIMESTAMP_FORMAT = '%m-%d-%Y %H:%M:%S', DATE_FORMAT = '%Y/%m/%d' ); ``` ## COPY DATABASE Beside copying specific table to/from some path, `COPY` statement can also be used to copy whole database to/from some path. The syntax for copying databases is: ```sql COPY DATABASE [TO | FROM] '' WITH ( FORMAT = { 'CSV' | 'JSON' | 'PARQUET' }, START_TIME = "", END_TIME = "", PARALLELISM = ) [CONNECTION( REGION = "", ENDPOINT = "", ACCESS_KEY_ID = "", SECRET_ACCESS_KEY = "", ENABLE_VIRTUAL_HOST_STYLE = "[true | false]", )] ``` | Option | Description | Required | |---|---|---| | `FORMAT` | Export file format, available options: JSON, CSV, Parquet | **Required** | | `START_TIME`/`END_TIME`| The time range within which data should be exported. `START_TIME` is inclusive and `END_TIME` is exclusive. | Optional | | `PARALLELISM` | Number of tables to process in parallel. For example, if a database contains 30 tables and `PARALLELISM` is set to 8, then 8 tables will be processed concurrently. Defaults to the total number of CPU cores, with a minimum value of 1. | Optional | > - When copying databases, `` must end with `/`. > - `CONNECTION` parameters can also be used to copying databases to/from object storage services like AWS S3, GCS, and Azure Blob Storage. ### Examples ```sql -- Export all tables' data to /tmp/export/ COPY DATABASE public TO '/tmp/export/' WITH (FORMAT='parquet'); -- Export all table data using 4 parallel operations COPY DATABASE public TO '/tmp/export/' WITH (FORMAT='parquet', PARALLELISM=4); -- Export all tables' data within time range 2022-04-11 08:00:00~2022-04-11 09:00:00 to /tmp/export/ COPY DATABASE public TO '/tmp/export/' WITH (FORMAT='parquet', START_TIME='2022-04-11 08:00:00', END_TIME='2022-04-11 09:00:00'); -- Import files under /tmp/export/ directory to database named public. COPY DATABASE public FROM '/tmp/export/' WITH (FORMAT='parquet'); -- Import files using 8 parallel operations COPY DATABASE public FROM '/tmp/export/' WITH (FORMAT='parquet', PARALLELISM=8); ``` ## Special reminder for Windows platforms Please notice that when executing `COPY`/`COPY DATABASE` statements on Windows platforms, backslashes (`\`) in paths should be replaced with `/` for compatibility. ```sql -- Won't work COPY tbl TO 'C:\xxx\xxx\output.parquet' WITH (FORMAT = 'parquet'); -- Correct path: COPY tbl TO 'C:/xxx/xxx/output.parquet' WITH (FORMAT = 'parquet'); ``` --- ## CREATE `CREATE` is used to create new databases or tables. ## CREATE DATABASE ### Syntax Creates a new database: ```sql CREATE DATABASE [IF NOT EXISTS] db_name [WITH ] ``` If the `db_name` database already exists, then GreptimeDB has the following behaviors: - Doesn't create a new database. - Doesn't return an error when the clause `IF NOT EXISTS` is presented. - Otherwise, returns an error. The database can also carry options similar to the `CREATE TABLE` statement by using the `WITH` keyword. The following options are available for databases: - `ttl` - Time-To-Live for data in all tables within the database (cannot be set to `instant`) - `memtable.type` - Type of memtable (`bulk`, `time_series`) - `append_mode` - Whether tables in the database should be append-only (`true`/`false`) - `merge_mode` - Strategy for merging duplicate rows (`last_row`, `last_non_null`) - `skip_wal` - Whether to disable Write-Ahead-Log for tables in the database (`'true'`/`'false'`) - `sst_format` - SST (Sorted String Table) file format for tables in the database (`flat`, `primary_key`) - `compaction.*` - Compaction-related settings (e.g., `compaction.type`, `compaction.twcs.time_window`) Read more about [table options](#table-options). :::note Important Behavior Differences Database options behave differently: - **TTL and Compaction options** (`ttl`, `compaction.*`): These options have ongoing effect. Tables without specified values will continuously inherit database-level values. Changing the database TTL or compaction options will immediately impact all tables that don't have their own settings. - **Other options** (`memtable.type`, `append_mode`, `merge_mode`, `skip_wal`, `sst_format`): These act as template variables that are only applied when creating new tables. Changing these database-level options will NOT affect existing tables - they only serve as defaults for newly created tables. ::: When creating a table, if the corresponding table options are not provided, the options configured at the database level will be applied. ### Examples Creates a `test` database: ```sql CREATE DATABASE test; ``` ```sql Query OK, 1 row affected (0.05 sec) ``` Creates it again with `IF NOT EXISTS`: ```sql CREATE DATABASE IF NOT EXISTS test; ``` Create a database with a `TTL` (Time-To-Live) of seven days, which means all the tables in this database will inherit this option if they don't have their own `TTL` setting: ```sql CREATE DATABASE test WITH (ttl='7d'); ``` Create a database with multiple options, including append mode and custom memtable type: ```sql CREATE DATABASE test WITH ( ttl='30d', 'memtable.type'='bulk', 'append_mode'='true' ); ``` Create a database with Write-Ahead-Log disabled and custom merge mode: ```sql CREATE DATABASE test WITH ( 'skip_wal'='true', 'merge_mode'='last_non_null' ); ``` Create a database with a specific SST file format: ```sql CREATE DATABASE test WITH ('sst_format'='flat'); ``` ## CREATE TABLE ### Syntax Creates a new table in the `db` database or the current database in use: ```sql CREATE TABLE [IF NOT EXISTS] [db.]table_name ( column1 type1 [NULL | NOT NULL] [DEFAULT expr1] [TIME INDEX] [PRIMARY KEY] [indexes] [COMMENT comment1], column2 type2 [NULL | NOT NULL] [DEFAULT expr2] [TIME INDEX] [PRIMARY KEY] [indexes] [COMMENT comment2], ... [TIME INDEX (column)], [PRIMARY KEY(column1, column2, ...)], ) [ PARTITION ON COLUMNS(column1, column2, ...) ( , ... ) ] ENGINE = engine WITH([TTL | storage | ...] = expr, ...) ``` The table schema is specified by the brackets before the `ENGINE`. The table schema is a list of column definitions and table constraints. For information on the `engine` option and table engine selection, please refer to the [Table Engines](/reference/about-greptimedb-engines.md) guide. A column definition includes the column `column_name`, `type`, and options such as nullable or default values, etc. Please see below. ### Table constraints The table constraints contain the following: - `TIME INDEX` specifies the time index column, which always has one and only one column. It indicates the `Timestamp` type in the [data model](/user-guide/concepts/data-model.md) of GreptimeDB. - `PRIMARY KEY` specifies the table's primary key column, which indicates the `Tag` type in the [data model](/user-guide/concepts/data-model.md) of GreptimeDB. It cannot include the time index column, but it always implicitly adds the time index column to the end of keys. - The Other columns are `Field` columns in the [data model](/user-guide/concepts/data-model.md) of GreptimeDB. :::tip NOTE The `PRIMARY KEY` columns and `TIME INDEX` together form the storage key used to order and merge rows. Unlike a primary key in a relational database, this storage key does not enforce uniqueness. The [`merge_mode`](#create-a-table-with-merge-mode) and [`append_mode`](#create-an-append-only-table) options determine how GreptimeDB handles rows with the same storage key. ::: The statement won't do anything if the table already exists and `IF NOT EXISTS` is presented; otherwise returns an error. #### Indexes GreptimeDB provides various type of indexes to accelerate query. Please refer to [Data Index](/user-guide/manage-data/data-index.md) for more details. ### Table options Users can add table options by using `WITH`. The valid options contain the following: | Option | Description | Value | | ------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ttl` | The storage time of the table data | A time duration string such as `'60m'`, `'1h'` for one hour, `'14d'` for 14 days etc. Supported time units are: `s` / `m` / `h` / `d`. | | `storage` | The name of the table storage engine provider | String value, such as `S3`, `Gcs`, etc. It must be configured in `[[storage.providers]]`, see [configuration](/user-guide/deployments-administration/configuration.md#storage-engine-provider). | | `compaction.type` | Compaction strategy of the table | String value. Only `twcs` is allowed. | | `compaction.twcs.trigger_file_num` | Number of files in a specific time window to trigger a compaction | String value, such as '8'. Only available when `compaction.type` is `twcs`. You can refer to this [document](https://cassandra.apache.org/doc/latest/cassandra/managing/operating/compaction/twcs.html) to learn more about the `twcs` compaction strategy. | | `compaction.twcs.time_window` | Compaction time window | String value, such as '1d' for 1 day. The table usually partitions rows into different time windows by their timestamps. Only available when `compaction.type` is `twcs`. | | `compaction.twcs.max_output_file_size` | Maximum allowed output file size for TWCS compaction | String value, such as '1GB', '512MB'. Sets the maximum size for files produced by TWCS compaction. Only available when `compaction.type` is `twcs`. | | `memtable.type` | Type of the memtable | String value: `bulk` or `time_series`. If omitted, Mito selects the implementation from the SST format; the default flat format uses `bulk`. Setting `bulk` forces `sst_format=flat`, and flat SSTs use the bulk implementation even if `time_series` is specified. The legacy value `partition_tree` is accepted for compatibility and maps to the bulk and flat path. | | `append_mode` | Whether the table is append-only | String value. Default is 'false', which removes duplicate rows by primary keys and timestamps according to the `merge_mode`. Setting it to 'true' to enable append mode and create an append-only table which keeps duplicate rows. | | `merge_mode` | The strategy to merge duplicate rows | String value. Only available when `append_mode` is 'false'. Default is `last_row`, which keeps the last row for the same primary key and timestamp. Setting it to `last_non_null` to keep the last non-null field for the same primary key and timestamp. | | `sst_format` | The format of SST files | String value, supports `primary_key`, `flat`. Default is `flat`. `flat` is recommended for tables which have a large number of unique primary keys. | | `comment` | Table level comment | String value. | | `skip_wal` | Whether to disable Write-Ahead-Log for this table | String type. When set to `'true'`, the data written to the table will not be persisted to the write-ahead log, which can avoid storage wear and improve write throughput. However, when the process restarts, any unflushed data will be lost. Please use this feature only when the data source itself can ensure reliability. | | `write_buffer_size` | Per-region write buffer stall threshold for this table | String type, such as `'512MB'` or `'1GB'`. For a positive value, GreptimeDB schedules a flush when mutable memtable usage reaches half the value, stalls writes at the value, and rejects writes at twice the value. The table option overrides `region_engine.mito.default_region_write_buffer_size`. An explicit `'0'` disables the per-region limit even when the engine default is nonzero. Unset the option to remove the table override and fall back to the engine default. | | `auto_flush_interval` | How long a region of this table may go without a flush before one is triggered | String type, a time duration such as `'5m'` or `'1h'`. Must be greater than zero. The table option overrides the engine-wide `region_engine.mito.auto_flush_interval`. Set it to `NULL` with `ALTER TABLE` to drop the override and fall back to the engine setting. | | `max_row_group_row_count` | Maximum number of rows in a Parquet row group | String type representing an integer from `1` through `10485760` (`10 * 1024 * 1024`). The default is `102400` (`100 * 1024`) when this option is not set. | | `index.type` | Index type | **Only for metric engine** String value, supports `inverted`, `skipping`. If not set, no index is created. | #### Create a table with a custom row group size ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10 ) WITH ('max_row_group_row_count' = '1024'); ``` The default row group size is recommended for most users. For advanced usage, benchmark different values against your workload to improve performance, or use a smaller value when a row group consumes too much memory. Smaller row groups may reduce memory usage and enable finer-grained pruning, but create more row groups and increase metadata overhead. Larger row groups make the opposite tradeoff. #### Create a table with TTL For example, to create a table with the storage data TTL(Time-To-Live) is seven days: ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10, ) with(ttl='7d'); ``` The `ttl` value can be one of the following: - A [duration](/reference/time-durations.md) like `1hour 12min 5s`. - `forever`, `NULL`, an empty string `''` and `0s` (or any zero length duration, like `0d`), means the data will never be deleted. - `instant`, note that database's TTL can't be set to `instant`. `instant` means the data will be deleted instantly when inserted. Avoid using `instant` TTL source tables for new Flow workloads because they fall back to the deprecated streaming mode; see the [flow management documents](/user-guide/flow-computation/manage-flow.md#create-a-source-table). - Unset, `ttl` can be unset by using `ALTER TABLE UNSET 'ttl'`, which means the table will inherit the database's ttl policy (if any). If a table has its own TTL policy, it will take precedence over the database TTL policy. Otherwise, the database TTL policy will be applied to the table. So if table's TTL is set to `forever`, no matter what the database's TTL is, the data will never be deleted. But if you unset table TTL using: ```sql ALTER TABLE UNSET 'ttl'; ``` Then the database's TTL will be applied to the table. Note that the default TTL setting for table and database is unset, which also means the data will never be deleted. #### Create a table with custom storage Create a table that stores the data in Google Cloud Storage: ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10, ) with(ttl='7d', storage="Gcs"); ``` #### Create a table with custom compaction options Create a table with custom compaction options. The table will attempt to partition data into 1-day time window based on the timestamps of the data and merges files within each time window if they exceed 8 files. ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10, ) with( 'compaction.type'='twcs', 'compaction.twcs.time_window'='1d', 'compaction.twcs.trigger_file_num'='8', 'compaction.twcs.max_output_file_size'='1GB' ); ``` #### Create an append-only table Create an append-only table which disables deduplication. ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10, ) with('append_mode'='true'); ``` #### Create a table with merge mode Create a table with `last_row` merge mode, which is the default merge mode. ```sql create table if not exists metrics( host string, ts timestamp, cpu double, memory double, TIME INDEX (ts), PRIMARY KEY(host) ) with('merge_mode'='last_row'); ``` Under `last_row` mode, the table merges rows with the same primary key and timestamp by only keeping the latest row. ```sql INSERT INTO metrics VALUES ('host1', 0, 0, NULL), ('host2', 1, NULL, 1); INSERT INTO metrics VALUES ('host1', 0, NULL, 10), ('host2', 1, 11, NULL); SELECT * from metrics ORDER BY host, ts; +-------+-------------------------+------+--------+ | host | ts | cpu | memory | +-------+-------------------------+------+--------+ | host1 | 1970-01-01T00:00:00 | | 10.0 | | host2 | 1970-01-01T00:00:00.001 | 11.0 | | +-------+-------------------------+------+--------+ ``` Create a table with `last_non_null` merge mode. ```sql create table if not exists metrics( host string, ts timestamp, cpu double, memory double, TIME INDEX (ts), PRIMARY KEY(host) ) with('merge_mode'='last_non_null'); ``` Under `last_non_null` mode, the table merges rows with the same primary key and timestamp by keeping the latest non-null value of each field. ```sql INSERT INTO metrics VALUES ('host1', 0, 0, NULL), ('host2', 1, NULL, 1); INSERT INTO metrics VALUES ('host1', 0, NULL, 10), ('host2', 1, 11, NULL); SELECT * from metrics ORDER BY host, ts; +-------+-------------------------+------+--------+ | host | ts | cpu | memory | +-------+-------------------------+------+--------+ | host1 | 1970-01-01T00:00:00 | 0.0 | 10.0 | | host2 | 1970-01-01T00:00:00.001 | 11.0 | 1.0 | +-------+-------------------------+------+--------+ ``` #### Create a table with WAL disabled Create a table with WAL disabled. Please note that when WAL is disabled, unflushed data will be lost on process restart. ```sql CREATE TABLE IF NOT EXISTS temperatures( ts TIMESTAMP TIME INDEX, temperature DOUBLE DEFAULT 10 ) with('skip_wal'='true'); ``` #### Create a physical table with metric engine The Metric Engine stores many logical metric tables in shared physical wide tables. This design reuses columns and metadata across logical tables. See the [Metric Engine architecture](/contributor-guide/datanode/metric-engine.md) and [Table Engines](/reference/about-greptimedb-engines.md). Create a physical table with the metric engine. ```sql CREATE TABLE greptime_physical_table ( greptime_timestamp TIMESTAMP(3) NOT NULL, greptime_value DOUBLE NULL, TIME INDEX (greptime_timestamp), ) engine = metric with ( "physical_metric_table" = "", ); ``` #### Create a physical table with enable skipping index for columns By default, the metric engine won't create indexes for columns. You can enable it by setting the `index.type` to `skipping`. Create a physical table with a skipping index; all automatically added columns will have this index applied. ```sql CREATE TABLE greptime_physical_table ( greptime_timestamp TIMESTAMP(3) NOT NULL, greptime_value DOUBLE NULL, TIME INDEX (greptime_timestamp), ) engine = metric with ( "physical_metric_table" = "", "index.type" = "skipping", ); ``` #### Create a table with SST format Create a table with `flat` SST format. ```sql CREATE TABLE IF NOT EXISTS metrics( host string, ts timestamp, cpu double, memory double, TIME INDEX (ts), PRIMARY KEY(host) ) with('sst_format'='flat'); ``` The `flat` format is optimized for high cardinality primary keys and is the default SST format for new tables. ### Column options GreptimeDB supports the following column options: | Option | Description | | ----------------- | ---------------------------------------------------------------------------------------------------------- | | NULL | The column value can be `null`. | | NOT NULL | The column value can't be `null`. | | DEFAULT `expr` | The column's default value is `expr`, which its result type must be the column's type | | COMMENT `comment` | The column comment. It must be a string value | | FULLTEXT INDEX | Creates a full-text index to speed up full-text search operations. Applicable only to string-type columns. | | SKIPPING INDEX | Creates a skipping index to speed up query on sparse data. | | INVERTED INDEX | Creates an inverted index to speed up query on dense data. | The table constraints `TIME INDEX` and `PRIMARY KEY` can also be set by column option, but they can only be specified once in column definitions. So you can't specify `PRIMARY KEY` for more than one column except by the table constraint `PRIMARY KEY` : ```sql CREATE TABLE system_metrics ( host STRING PRIMARY KEY, idc STRING PRIMARY KEY, cpu_util DOUBLE, memory_util DOUBLE, disk_util DOUBLE, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), TIME INDEX(ts) ); ``` Goes wrong: ```sql Illegal primary keys definition: not allowed to inline multiple primary keys in columns options ``` ```sql CREATE TABLE system_metrics ( host STRING, idc STRING, cpu_util DOUBLE, memory_util DOUBLE, disk_util DOUBLE, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, PRIMARY KEY(host, idc), ); ``` ```sql Query OK, 0 rows affected (0.01 sec) ``` #### `INDEX` Column Option For more information on the `INDEX` column option, please refer to the [Data Index](/user-guide/manage-data/data-index.md) document. ### Region partition rules Please refer to [Partition](/user-guide/deployments-administration/manage-data/table-sharding.md#partition) for more details. ## CREATE EXTERNAL TABLE ### Syntax Creates a new file external table in the `db` database or the current database in use: ```sql CREATE EXTERNAL TABLE [IF NOT EXISTS] [db.]table_name [ ( column1 type1 [NULL | NOT NULL] [DEFAULT expr1] [TIME INDEX] [PRIMARY KEY] [COMMENT comment1], column2 type2 [NULL | NOT NULL] [DEFAULT expr2] [TIME INDEX] [PRIMARY KEY] [COMMENT comment2], ... [TIME INDEX (column)], [PRIMARY KEY(column1, column2, ...)] ) ] WITH ( LOCATION = url, FORMAT = { 'CSV' | 'JSON' | 'PARQUET' | 'ORC' } [,PATTERN = regex_pattern ] [,REGION = region ] [,ENDPOINT = uri ] [,ACCESS_KEY_ID = key_id ] [,SECRET_ACCESS_KEY = access_key ] [,ENABLE_VIRTUAL_HOST_STYLE = { TRUE | FALSE }] [,SESSION_TOKEN = token ] ... ) ``` ### Table options | Option | Description | Required | | ---------- | ------------------------------------------------------------------------------- | ------------ | | `LOCATION` | External files locations, e.g., `s3://[]`, `//[]` | **Required** | | `FORMAT` | Target file(s) format, e.g., JSON, CSV, Parquet, ORC | **Required** | | `PATTERN` | Use regex to match files. e.g., `*_today.parquet` | Optional | #### S3 | Option | Description | Required | | --------------------------- | ------------------------------------------------------------------------------------- | ------------ | | `REGION` | AWS region name. e.g., us-east-1. | **Required** | | `ENDPOINT` | The bucket endpoint | Optional | | `ACCESS_KEY_ID` | ACCESS_KEY_ID Your access key ID for connecting the AWS S3 compatible object storage. | Optional | | `SECRET_ACCESS_KEY` | Your secret access key for connecting the AWS S3 compatible object storage. | Optional | | `ENABLE_VIRTUAL_HOST_STYLE` | If you use virtual hosting to address the bucket, set it to "true". | Optional | | `SESSION_TOKEN` | Your temporary credential for connecting the AWS S3 service. | Optional | ### Time Index Column When creating an external table using the `CREATE EXTERNAL TABLE` statement, you are required to use the `TIME INDEX` constraint to specify a Time Index column. ### Examples For nested JSON objects, omitting column definitions allows them to be inferred as `Struct` columns, whose fields can be accessed with brackets. See [Query nested JSON fields](/user-guide/query-data/query-external-data.md#query-nested-json-fields). When columns are explicitly declared, additional fields in the files are not automatically added to the table definition. You can create an external table without columns definitions, the column definitions will be automatically inferred: ```sql CREATE EXTERNAL TABLE IF NOT EXISTS city WITH (location='/var/data/city.csv',format='csv'); ``` In this example, we did not explicitly define the columns of the table. To satisfy the requirement that the external table must specify a **Time Index** column, the `CREATE EXTERNAL TABLE` statement will infer the Time Index column according to the following rules: 1. If the Time Index column can be inferred from the file metadata, then that column will be used as the Time Index column. 2. If there is a column named `greptime_timestamp` (the type of this column must be `TIMESTAMP`, otherwise, an error will be thrown), then this column will be used as the Time Index column. 3. Otherwise, a column named `greptime_timestamp` will be automatically created as the Time Index column, and a `DEFAULT '1970-01-01 00:00:00+0000'` constraint will be added. Or ```sql CREATE EXTERNAL TABLE city ( host string, ts timestamp, cpu float64 default 0, memory float64, TIME INDEX (ts), PRIMARY KEY(host) ) WITH (location='/var/data/city.csv', format='csv'); ``` In this example, we explicitly defined the `ts` column as the Time Index column. If there is no suitable Time Index column in the file, you can also create a placeholder column and add a `DEFAULT expr` constraint. ## CREATE FLOW ```sql CREATE [OR REPLACE] FLOW [ IF NOT EXISTS ] SINK TO [ EXPIRE AFTER ] [ EVAL INTERVAL ] [ COMMENT '' ] [ WITH ( = [, ...]) ] AS ; ``` The `WITH` clause specifies flow options. For example, the experimental `experimental_enable_incremental_read` option enables incremental source reads for eligible batching flows. For `CREATE FLOW`, the query after `AS` can be a regular flow query or a TQL query. GreptimeDB also supports a strict TQL CTE form for cleaner flow definitions: ```sql CREATE FLOW calc_rate_cte SINK TO rate_reqs EVAL INTERVAL '1m' AS WITH rate_data (ts, req_rate, host, job, instance) AS ( TQL EVAL (now() - '1m'::interval, now(), '30s') rate(http_requests_total{job="my_service"}[1m]) AS req_rate ) SELECT * FROM rate_data; ``` When using `WITH` in `CREATE FLOW`, the accepted shape is intentionally strict: - Only one CTE is allowed, and it must contain `TQL EVAL`. - The outer query must be exactly `SELECT * FROM `. - Additional projection, filtering, joins, ordering, or extra SQL CTEs are not supported. - If the CTE name is quoted, reference it with the same quoted name in the outer `SELECT`. For the statement to create or update a flow, please read the [flow management documents](/user-guide/flow-computation/manage-flow.md#create-a-flow). ## CREATE VIEW ```sql CREATE [OR REPLACE] VIEW [ IF NOT EXISTS ] AS select_statement ``` For the statement to create or update a view, please read the [view user guide](/user-guide/query-data/view.md). ## CREATE TRIGGER Please refer to the [CREATE TRIGGER](/reference/sql/trigger-syntax.md#create-trigger) documentation. --- ## Data Types SQL data types define the type of data that a column can store. When you run the `DESC TABLE` command, you can see the data type of each column. ## String and Binary Data Types | Type Name | Description | Size | | --------- | ---------------------------------------------------------------------- | -------------------------------- | | `String` | UTF-8 encoded strings. Holds up to 2,147,483,647 bytes of data | The length of the strings | | `Binary` | Variable-length binary values. Holds up to 2,147,483,647 bytes of data | The length of the data + 2 bytes | The maximum capacities of `String` and `Binary` are determined by their encodings and how the storage engine handles them. For example, `String` values are encoded into UTF-8. If all characters are 3 bytes in length, this field can store up to 715,827,882 characters. As for `Binary` types, they can store a maximum of 2,147,483,647 bytes. ## Numeric Data Types | Type Name | Description | Size | | --------- | ----------------------------------------------- | ------- | | `Int8` | -128 ~ 127 | 1 Byte | | `Int16` | -32768 ~ 32767 | 2 Bytes | | `Int32` | -2147483648 ~ 2147483647 | 4 Bytes | | `Int64` | -9223372036854775808 ~ 9223372036854775807 | 8 Bytes | | `UInt8` | 0 ~ 255 | 1 Byte | | `UInt16` | 0 ~ 65535 | 2 Bytes | | `UInt32` | 0 ~ 4294967295 | 4 Bytes | | `UInt64` | 0 ~ 18446744073709551615 | 8 Bytes | | `Float32` | 32-bit IEEE754 floating point values | 4 Bytes | | `Float64` | Double precision IEEE 754 floating point values | 8 Bytes | :::tip NOTE The descriptions here refer to **GreptimeDB native type information**, which are measured in **bits**. However, when using **SQL**, follow the conventions of **PostgreSQL** and **MySQL**, where types like `INT2`, `INT4`, `INT8`, `FLOAT4` and `FLOAT8` are defined in **bytes**. For example, in an SQL statement, `INT8` actually corresponds to **BigInt** (8 bytes, 64 bits). ::: ## Decimal Type GreptimeDB supports the `decimal` type, a fixed-point type represented as `decimal(precision, scale)`, where `precision` is the total number of digits, and `scale` is the number of digits in the fractional part. For example, `123.45` has a precision of 5 and a scale of 2. - `precision` can range from [1, 38]. - `scale` can range from [0, precision]. The default decimal is `decimal(38, 10)` if the precision and scale are not specified. ```sql CREATE TABLE decimals( d DECIMAL(3, 2), ts TIMESTAMP TIME INDEX, ); INSERT INTO decimals VALUES ('0.1',1000), ('0.2',2000); SELECT * FROM decimals; ``` Output: ```sql +------+---------------------+ | d | ts | +------+---------------------+ | 0.10 | 1970-01-01T00:00:01 | | 0.20 | 1970-01-01T00:00:02 | +------+---------------------+ ``` ## Date and Time Types | Type Name | Description | Size | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | `TimestampSecond` | 64-bit timestamp values with seconds precision, range: `[-262144-01-01 00:00:00, +262143-12-31 23:59:59]` | 8 Bytes | | `TimestampMillisecond` | 64-bit timestamp values with milliseconds precision, range: `[-262144-01-01 00:00:00.000, +262143-12-31 23:59:59.999]` | 8 Bytes | | `TimestampMicroSecond` | 64-bit timestamp values with microseconds precision, range: `[-262144-01-01 00:00:00.000000, +262143-12-31 23:59:59.999999]` | 8 Bytes | | `TimestampNanosecond` | 64-bit timestamp values with nanoseconds precision, range: `[1677-09-21 00:12:43.145225, 2262-04-11 23:47:16.854775807]` | 8 Bytes | | `Interval` | Time interval | 4 Bytes for `YearMonth`, 8 Bytes for `DayTime` and 16 Bytes for `MonthDayNano` | :::tip NOTE When inserting Timestamp string literals to GreptimeDB using MySQL/PostgreSQL protocol, the value range is limited to '0001-01-01 00:00:00' to '9999-12-31 23:59:59'. ::: ### Interval Type `Interval` type is used in scenarios where you need to track and manipulate time durations. It can be written using the following verbose syntax: ``` QUANTITY UNIT [QUANTITY UNIT...] ``` * `QUANTITY`: is a number (possibly signed), * `UNIT`: is `microsecond`, `millisecond`, `second`, `minute`, `hour`, `day`, `week`, `month`, `year`, `decade`, `century`, or abbreviations or plurals of these units; The amounts of the different units are combined, with each unit's sign determining if it adds or subtracts from the total interval. For example, '1 year -2 months' results in a net interval of ten months. Unfortunately, GreptimeDB doesn't support writing the interval in the format of [ISO 8601 time intervals](https://en.wikipedia.org/wiki/ISO_8601#Time_intervals) such as `P3Y3M700DT133H17M36.789S` etc. But it's output supports it. Let's take some examples: ```sql SELECT '2 years 15 months 100 weeks 99 hours 123456789 milliseconds'::INTERVAL; ``` ```sql +---------------------------------------------------------------------+ | Utf8("2 years 15 months 100 weeks 99 hours 123456789 milliseconds") | +---------------------------------------------------------------------+ | P3Y3M700DT133H17M36.789S | +---------------------------------------------------------------------+ ``` 55 minutes ago: ```sql SELECT '-1 hour 5 minute'::INTERVAL; ``` ```sql +--------------------------+ | Utf8("-1 hour 5 minute") | +--------------------------+ | P0Y0M0DT0H-55M0S | +--------------------------+ ``` 1 hour and 5 minutes ago: ```sql SELECT '-1 hour -5 minute'::INTERVAL; ``` ```sql +---------------------------+ | Utf8("-1 hour -5 minute") | +---------------------------+ | P0Y0M0DT-1H-5M0S | +---------------------------+ ``` And of course, you can manipulate time with intervals by arithmetics. Get the time of 5 minutes go: ```sql SELECT now() - '5 minute'::INTERVAL; ``` ```sql +----------------------------------------------+ | now() - IntervalMonthDayNano("300000000000") | +----------------------------------------------+ | 2024-06-24 21:24:05.012306 | +----------------------------------------------+ ``` GreptimeDB also supports shorthand forms without spaces, such as `3y2mon4h`: ```sql SELECT '3y2mon4h'::INTERVAL; ``` ``` +---------------------------------------------------------+ | IntervalMonthDayNano("3010670175542044842954670112768") | +---------------------------------------------------------+ | P3Y2M0DT4H0M0S | +---------------------------------------------------------+ ``` It also supports signed numbers: ```sql SELECT '-1h5m'::INTERVAL; ``` ``` +----------------------------------------------+ | IntervalMonthDayNano("18446740773709551616") | +----------------------------------------------+ | P0Y0M0DT0H-55M0S | +----------------------------------------------+ ``` Supported abbreviations include: | Abbreviation | Full name | | ------------ | ------------ | | y | years | | mon | months | | w | weeks | | d | days | | h | hours | | m | minutes | | s | seconds | | millis | milliseconds | | ms | milliseconds | | us | microseconds | | ns | nanoseconds | #### INTERVAL keyword In the examples above, we used the cast operation `'{quantity unit}'::INTERVAL` to demonstrate the interval type. In fact, the interval type can also be used with the syntax supported by the `INTERVAL` keyword, though the behavior varies between database dialects: 1. In MySQL, the syntax is `INTERVAL {quantity} {unit}`, where `quantity` can be a number or a string depending on the context. For example: ```sql mysql> SELECT INTERVAL 1 YEAR; +--------------------------------------------------------------------------------------+ | IntervalMonthDayNano("IntervalMonthDayNano { months: 12, days: 0, nanoseconds: 0 }") | +--------------------------------------------------------------------------------------+ | P1Y0M0DT0H0M0S | +--------------------------------------------------------------------------------------+ 1 row in set (0.01 sec) mysql> SELECT INTERVAL '1 YEAR 2' MONTH; +--------------------------------------------------------------------------------------+ | IntervalMonthDayNano("IntervalMonthDayNano { months: 14, days: 0, nanoseconds: 0 }") | +--------------------------------------------------------------------------------------+ | P1Y2M0DT0H0M0S | +--------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) ``` 2. In PostgreSQL and the default GreptimeDB dialect, it is `INTERVAL '{quantity unit}'`, where the INTERVAL keyword is followed by the interval string: ```sql public=> SELECT INTERVAL '1 year'; IntervalMonthDayNano("IntervalMonthDayNano { months: 12, days: 0, nanoseconds: 0 }") -------------------------------------------------------------------------------------- 1 year (1 row) public=> SELECT INTERVAL '1 year 2 month'; IntervalMonthDayNano("IntervalMonthDayNano { months: 14, days: 0, nanoseconds: 0 }") -------------------------------------------------------------------------------------- 1 year 2 mons (1 row) ``` ## JSON Type (Experimental) :::warning The JSON feature is currently experimental and may change in future releases. ::: The `JSON` type stores and queries structured or unstructured JSON values. It is suitable for logs, analytics, and other semi-structured data, especially objects and arrays whose fields do not need separate table columns. ```sql CREATE TABLE json_data( my_json JSON, ts TIMESTAMP TIME INDEX ); INSERT INTO json_data VALUES ('{"key1": "value1", "key2": 10}', 1000), ('{"name": "GreptimeDB", "open_source": true}', 2000); SELECT * FROM json_data; ``` Output: ``` +------------------------------------------+---------------------+ | my_json | ts | +------------------------------------------+---------------------+ | {"key1":"value1","key2":10} | 1970-01-01 00:00:01 | | {"name":"GreptimeDB","open_source":true} | 1970-01-01 00:00:02 | +------------------------------------------+---------------------+ ``` ### Query JSON data You can query the JSON data directly or extract specific fields using [JSON functions](./functions/json.md) provided by GreptimeDB. Here's an example: ```sql SELECT json_get_string(my_json, '$.name') as name FROM json_data; ``` Output: ``` +---------------------------------------------------+ | name | +---------------------------------------------------+ | NULL | | GreptimeDB | +---------------------------------------------------+ ``` ## JSON2 Type (Beta) `JSON2` stores fields from JSON objects in a structured, columnar layout while preserving fields with dynamic schemas. It is intended for frequently queried logs and other semi-structured data. JSON2 columns require an append-only table, and each non-NULL root value must be a non-empty JSON object. For table syntax, type hints, path access, storage settings, and current limitations, see the [JSON2 type documentation](/user-guide/logs/json2.md). ## Boolean Type | Type Name | Description | Size | | --------- | ----------- | ------ | | `Boolean` | Bool values | 1 Byte | Use `TRUE` or `FALSE` to represent boolean values in SQL statements. For example: ```sql CREATE TABLE bools( b BOOLEAN, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, ); ``` ```sql INSERT INTO bools(b, ts) VALUES (TRUE, '2024-01-01 00:00:00'), (FALSE, '2024-01-01 00:00:01'); ``` ## Data types compatible with MySQL and PostgreSQL ### Type aliases For users migrating from MySQL or PostgreSQL to GreptimeDB, GreptimeDB supports the following alias types. | SQL Datatype Alias | Native Datatype | | --------------------------------------------------------------- | ---------------------- | | `Text`, `TinyText`, `MediumText`, `LongText`, `Varchar`, `Char` | `String` | | `Varbinary` | `Binary` | | `TinyInt` | `Int8` | | `SmallInt`, `Int2` | `Int16` | | `Int`, `Int4` | `Int32` | | `BigInt`, `Int8` | `Int64` | | `TinyInt Unsigned` | `UInt8` | | `SmallInt Unsigned` | `UInt16` | | `Int Unsigned` | `UInt32` | | `BigInt Unsigned` | `UInt64` | | `Float`, `Float4` | `Float32` | | `Double`, `Float8` | `Float64` | | `Timestamp_s`, `Timestamp_sec`, `Timestamp(0)` | `TimestampSecond` | | `Timestamp`, `Timestamp_ms`, `Timestamp(3)` | `TimestampMillisecond` | | `Timestamp_us`, `Timestamp(6)` | `TimestampMicroSecond` | | `Timestamp_ns`, `Timestamp(9)` | `TimestampNanosecond` | :::warning Note The type aliases `Int2`, `Int4`, `Int8`, `Float4`, and `Float8` follow the PostgreSQL and MySQL convention where these identifiers refer to the number of **bytes** (not bits) in the type. Specifically: - `Int2` = 2 bytes = `SmallInt` (16-bit) - `Int4` = 4 bytes = `Int` (32-bit) - `Int8` = 8 bytes = `BigInt` (64-bit) - `Float4` = 4 bytes = `Float` (32-bit) - `Float8` = 8 bytes = `Double` (64-bit) Note: GreptimeDB's native type names (like `UInt8`, `Int32`, `Int64`) refer to the number of **bits**, while the SQL aliases `Int2`, `Int4`, and `Int8` refer to the number of **bytes** following PostgreSQL/MySQL conventions. For example, the native type `Int8` is an 8-**bit** integer (`TinyInt`, 1 byte), while the SQL alias `INT8` maps to an 8-**byte** integer (`BigInt`, 64-bit). ::: You can use these alias types when creating tables. For example, use `Varchar` instead of `String`, `Double` instead of `Float64`, and `Timestamp(0)` instead of `TimestampSecond`. ```sql CREATE TABLE alias_types ( s TEXT, i DOUBLE, ts0 TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP() TIME INDEX, PRIMARY KEY(s) ); ``` ### Date and time types In addition to the `Timestamp` types used as the default time type in GreptimeDB, GreptimeDB also supports `Date` and `DateTime` types for compatibility with MySQL and PostgreSQL. | Type name | Description | Size | | ---------- | ----------------------------------------------------------------------------------------- | ------- | | `Date` | 32-bit date values represent the days since UNIX Epoch | 4 Bytes | | `DateTime` | 64-bit timestamp values with milliseconds precision, equivalent to `TimestampMicrosecond` | 8 Bytes | ## Examples ### Create Table ```sql CREATE TABLE data_types ( s STRING, vbi BINARY, b BOOLEAN, tint INT8, sint INT16, i INT32, bint INT64, utint UINT8, usint UINT16, ui UINT32, ubint UINT64, f FLOAT32, d FLOAT64, dm DECIMAL(3, 2), dt DATE, dtt DATETIME, ts0 TIMESTAMPSECOND, ts3 TIMESTAMPMILLISECOND, ts6 TIMESTAMPMICROSECOND, ts9 TIMESTAMPNANOSECOND DEFAULT CURRENT_TIMESTAMP() TIME INDEX, PRIMARY KEY(s)); ``` ### Describe Table ```sql DESC TABLE data_types; ``` ```sql +--------+----------------------+------+------+---------------------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------+----------------------+------+------+---------------------+---------------+ | s | String | PRI | YES | | TAG | | vbi | Binary | | YES | | FIELD | | b | Boolean | | YES | | FIELD | | tint | Int8 | | YES | | FIELD | | sint | Int16 | | YES | | FIELD | | i | Int32 | | YES | | FIELD | | bint | Int64 | | YES | | FIELD | | utint | UInt8 | | YES | | FIELD | | usint | UInt16 | | YES | | FIELD | | ui | UInt32 | | YES | | FIELD | | ubint | UInt64 | | YES | | FIELD | | f | Float32 | | YES | | FIELD | | d | Float64 | | YES | | FIELD | | dm | Decimal(3, 2) | | YES | | FIELD | | dt | Date | | YES | | FIELD | | dtt | TimestampMicrosecond | | YES | | FIELD | | ts0 | TimestampSecond | | YES | | FIELD | | ts3 | TimestampMillisecond | | YES | | FIELD | | ts6 | TimestampMicrosecond | | YES | | FIELD | | ts9 | TimestampNanosecond | PRI | NO | current_timestamp() | TIMESTAMP | +--------+----------------------+------+------+---------------------+---------------+ ``` --- ## DELETE `DELETE` is used to remove rows from a table. ## Syntax ```sql DELETE FROM [db.]table WHERE expr ``` It removes rows from the table `[db.]table` that satisfies the expression `expr` after `WHERE`. The removed rows are marked immediately and can't be retrieved by all subsequent queries. ## Example For example, there is a table with the primary key `host`: ```sql CREATE TABLE monitor ( host STRING, ts TIMESTAMP, cpu DOUBLE DEFAULT 0, memory DOUBLE, TIME INDEX (ts), PRIMARY KEY(host)) ; ``` To delete a row from it by primary key `host` and timestamp index `ts`: ```sql DELETE FROM monitor WHERE host = 'host1' and ts = 1655276557000; ``` --- ## DESCRIBE TABLE `DESCRIBE [TABLE] [db.]table` describes the table structure in the `db` or the current database in-use. ## Examples Describes the table `monitor`: ```sql DESCRIBE TABLE monitor; ``` or ```sql DESCRIBE monitor; ``` Output: ```sql +--------+----------------------+------+------+---------------------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------+----------------------+------+------+---------------------+---------------+ | host | String | PRI | YES | | TAG | | ts | TimestampMillisecond | PRI | NO | current_timestamp() | TIMESTAMP | | cpu | Float64 | | YES | 0 | FIELD | | memory | Float64 | | YES | | FIELD | +--------+----------------------+------+------+---------------------+---------------+ 4 rows in set (0.00 sec) ``` It produces the table structure: * `Column`: the column names * `Type`: the column data types * `Key`: `PRI` means the column is in the primary key constraint. * `Null`: `YES` means nullable, otherwise `NO` * `Default`: default value or expression of the column * `Semantic Type`: This column represents the semantic type, corresponding to `TAG`, `FIELD` or `TIMESTAMP` in the data model. --- ## DISTINCT `SELECT DISTINCT` removes duplicate rows from a query result. When the select list contains multiple expressions, rows are considered duplicates only when the complete set of selected values is equal. The following query returns each distinct `idc` value: ```sql SELECT DISTINCT idc FROM system_metrics; ``` `DISTINCT` is applied to the rows that satisfy the `WHERE` clause: ```sql SELECT DISTINCT idc, host FROM system_metrics WHERE host != 'host2'; ``` --- ## DROP :::tip GreptimeDB Enterprise provides the [Query Guard](/enterprise/deployments-administration/query-guard.md) plugin, which can completely ban `DROP TABLE` / `DROP DATABASE` statements for all users, including administrators. ::: ## DROP DATABASE `DROP DATABASE` drops a database. It removes the catalog entries for the database and deletes the directory containing the data. :::danger Danger `DROP DATABASE` cannot be undone. Use it with care! ::: ### Syntax ```sql DROP DATABASE [ IF EXISTS ] db_name ``` - `IF EXISTS`: Do not throw an error if the database does not exist. - `db_name`: The name of the database to remove. ### Examples To drop a database named `test`: ```sql DROP DATABASE test; ``` ## DROP TABLE `DROP TABLE` removes tables from the database. By default, it removes the table definition and all table data, indexes, rules, and constraints for that table. If [soft-drop](/enterprise/soft-drop.md) (GreptimeDB Enterprise only) is enabled in a distributed cluster, supported tables are moved to the recycle bin and can be restored before the retention deadline. :::danger Danger Without soft-drop, `DROP TABLE` cannot be undone. Use it with care! ::: ### Syntax ```sql DROP TABLE [ IF EXISTS ] table_name ``` - `IF EXISTS`: Do not throw an error if the table does not exist. - `table_name`: The name of the table to remove. ### Examples Drop the table `monitor` in the current database: ```sql DROP TABLE monitor; ``` ## UNDROP TABLE :::tip NOTE This feature is only available in the GreptimeDB Enterprise database. ::: `UNDROP TABLE` restores a soft-dropped table from the recycle bin. ### Syntax ```sql UNDROP TABLE table_name ``` - `table_name`: The unqualified, schema-qualified, or fully qualified name of the table to restore. `UNDROP TABLE` fails if a live table with the same full name already exists. ### Examples Restore the table `monitor` in the current database: ```sql UNDROP TABLE monitor; ``` Restore a table by its fully qualified name: ```sql UNDROP TABLE greptime.public.monitor; ``` ## DROP FLOW ```sql DROP FLOW [ IF EXISTS ] flow_name; ``` - `IF EXISTS`: Do not throw an error if the flow does not exist. - `flow_name`: The name of the flow to destroy. ```sql DROP FLOW IF EXISTS test_flow; ``` ``` Query OK, 0 rows affected (0.00 sec) ``` ## DROP VIEW ```sql DROP VIEW [ IF EXISTS ] view_name; ``` - `IF EXISTS`: Do not throw an error if the view does not exist. - `view_name`: The name of the view to remove. ```sql DROP VIEW IF EXISTS test_view; ``` ``` Query OK, 0 rows affected (0.00 sec) ``` ## DROP TRIGGER Please refer to the [Trigger syntax](/reference/sql/trigger-syntax.md#drop-trigger) documentation. --- ## EXPLAIN `EXPLAIN` is used to provide the execution plan of a statement. ## Syntax ```sql EXPLAIN [ANALYZE] [VERBOSE] SELECT ... ``` The `ANALYZE` clause will execute the statement and measure time spent at each plan node and the total rows of the output etc. The `VERBOSE` clause will provide more detailed information about the execution plan. ## Examples Explains the following query: ```sql EXPLAIN SELECT * FROM monitor where host='host1'\G ``` Example: ```sql *************************** 1. row *************************** plan_type: logical_plan plan: MergeScan [is_placeholder=false] *************************** 2. row *************************** plan_type: physical_plan plan: MergeScanExec: peers=[4612794875904(1074, 0), ] ``` The column `plan_type` indicates whether it's a`logical_plan` or `physical_plan`. And the column `plan` explains the plan in detail. The `MergeScan` plan merges the results from multiple regions. The `peers` array in the `MergeScanExec` physical plan contains the IDs of the regions that the plan will scan. Explains the execution of the plan by `ANALYZE`: ```sql EXPLAIN ANALYZE SELECT * FROM monitor where host='host1'\G ``` Example: ```sql *************************** 1. row *************************** stage: 0 node: 0 plan: MergeScanExec: peers=[4612794875904(1074, 0), ] metrics=[output_rows: 0, greptime_exec_read_cost: 0, finish_time: 3301415, first_consume_time: 3299166, ready_time: 3104209, ] *************************** 2. row *************************** stage: 1 node: 0 plan: SeqScan: region=4612794875904(1074, 0), partition_count=0 (0 memtable ranges, 0 file 0 ranges) metrics=[output_rows: 0, mem_used: 0, build_parts_cost: 1, build_reader_cost: 1, elapsed_await: 1, elapsed_poll: 21250, scan_cost: 1, yield_cost: 1, ] *************************** 3. row *************************** stage: NULL node: NULL plan: Total rows: 0 ``` The `EXPLAIN ANALYZE` command provides metrics about the execution of each stage. The `SeqScan` plan scans the data from a single region. Explains the verbose information of the plan execution: ```sql EXPLAIN ANALYZE VERBOSE SELECT * FROM monitor where host='host1'; ``` Example: ```sql *************************** 1. row *************************** stage: 0 node: 0 plan: MergeScanExec: peers=[4612794875904(1074, 0), ] metrics=[output_rows: 0, greptime_exec_read_cost: 0, finish_time: 3479084, first_consume_time: 3476000, ready_time: 3209041, ] *************************** 2. row *************************** stage: 1 node: 0 plan: SeqScan: region=4612794875904(1074, 0), partition_count=0 (0 memtable ranges, 0 file 0 ranges), projection=["host", "ts", "cpu", "memory"], filters=[host = Utf8("host1")], metrics_per_partition: [[partition=0, {prepare_scan_cost=579.75µs, build_reader_cost=0ns, scan_cost=0ns, convert_cost=0ns, yield_cost=0ns, total_cost=789.708µs, num_rows=0, num_batches=0, num_mem_ranges=0, num_file_ranges=0, build_parts_cost=0ns, rg_total=0, rg_fulltext_filtered=0, rg_inverted_filtered=0, rg_minmax_filtered=0, rg_bloom_filtered=0, rows_before_filter=0, rows_fulltext_filtered=0, rows_inverted_filtered=0, rows_bloom_filtered=0, rows_precise_filtered=0, num_sst_record_batches=0, num_sst_batches=0, num_sst_rows=0, first_poll=785.041µs}]] metrics=[output_rows: 0, mem_used: 0, build_parts_cost: 1, build_reader_cost: 1, elapsed_await: 1, elapsed_poll: 17208, scan_cost: 1, yield_cost: 1, ] *************************** 3. row *************************** stage: NULL node: NULL plan: Total rows: 0 ``` The `EXPLAIN ANALYZE VERBOSE` command provides the detail metrics about the execution of scan plans. ## Scanner Metrics Scanner nodes, including `SeqScan`, `SeriesScan`, and `UnorderedScan`, can print scanner-specific metrics in `EXPLAIN ANALYZE` and `EXPLAIN ANALYZE VERBOSE` output. These metrics help diagnose read-path behavior such as partitioning, SST pruning, index usage, cache behavior, and scanner timing. Verbose scanner metrics are diagnostic text, not a stable public API. Optional zero-valued fields are usually omitted. ### Output Layout A scanner line can contain three layers of information: - Scanner display fields before the trailing `metrics=[...]` section. - `metrics_per_partition`, which is printed only in verbose mode and keeps each partition's `PartitionMetrics` snapshot separate. - DataFusion aggregate metrics in the trailing `metrics=[...]` section. In `metrics_per_partition`, partition means a DataFusion execution-plan output partition. It is not a GreptimeDB region and not a table partition created by `PARTITION ON COLUMNS(...)`. One scanner metric partition may cover one or more GreptimeDB scan ranges. Normal mode layout: ```text : region=, metrics=[] ``` Verbose mode layout: ```text : region=, { "partition_count": {...}, "projection": [...], "filters": [...], "dyn_filters": [...], "files": [...], "flat_format": true, "metrics_per_partition": [ { "partition": , "metrics": { , "fetch_metrics": {...}, "metadata_cache_metrics": {...}, "top_file_metrics": {...} } } ] } metrics=[] ``` ### Aggregate Metrics The trailing `metrics=[...]` section is DataFusion's aggregate output for the scanner plan node. Scanner partitions record these values through `ExecutionPlanMetricsSet`, and the plan output aggregates them across partitions. The exact aggregate fields can vary by version and execution path. The output may include additional runtime fields such as `mem_used`, `elapsed_poll`, and `elapsed_await`. | Metric | Meaning | | --- | --- | | `output_rows` | Rows returned by the scanner plan node. | | `elapsed_compute` | Aggregate scanner compute time reported to DataFusion. | | `build_parts_cost` | Aggregate time spent building SST file ranges. | | `build_reader_cost` | Aggregate time spent building readers or merge readers. | | `convert_cost` | Aggregate time spent converting batches into Arrow record batches. | | `scan_cost` | Aggregate time spent scanning data from scanner inputs. | | `yield_cost` | Aggregate time spent waiting while yielding batches downstream. | Example with synthetic values: ```text SeqScan: region=0(1, 0), "partition_count":{"count":2, "mem_ranges":1, "files":1, "file_ranges":1} metrics=[output_rows: 128, elapsed_compute: 12ms, build_parts_cost: 1.2ms, build_reader_cost: 2.1ms, convert_cost: 300µs, scan_cost: 8.4ms, yield_cost: 900µs, ] ``` ### Scanner Display Fields The scanner display fields describe what the scanner will read and which predicates or projections are attached to it. `EXPLAIN ANALYZE` prints only the non-verbose fields. `EXPLAIN ANALYZE VERBOSE` adds detailed scanner input and per-partition metrics. | Field | Mode | Meaning | When present | | --- | --- | --- | --- | | Scanner name | All | The physical scanner, such as `SeqScan`, `SeriesScan`, or `UnorderedScan`. | Always. | | `region` | All | Region ID scanned by this plan node. | Always. | | `partition_count.count` | All | Number of partition ranges in the scanner. | Always. | | `partition_count.mem_ranges` | All | Number of memtable ranges in all partition ranges. | Always. | | `partition_count.files` | All | Number of SST files in the scan input before range expansion. | Always. | | `partition_count.file_ranges` | All | Number of SST file ranges in all partition ranges. | Always. | | `partition_count.other_ranges` | All | Number of extension or non-memtable, non-SST ranges. | Nonzero only. | | `selector` | All | Series row selector attached to the scan. | When a series row selector is attached. | | `distribution` | All | Distribution information attached to the scan. | When distribution is attached. | | `projection` | Verbose | Output column names after projection pruning. | When the output schema is not empty. | | `filters` | Verbose | Static physical predicate expressions pushed into the scanner. These predicates may drive row-group pruning, index application, and precise filtering. | When static predicates exist. | | `dyn_filters` | Verbose | Dynamic filter expressions attached after plan creation. These can change during execution as upstream operators produce filter values. | When dynamic filters exist. | | `vector_index_k` | Verbose | Vector index top-k value used by vector search. | When the vector index feature is enabled and the scan uses vector index search. | | `files` | Verbose | SST file metadata for files in the scan input. | When SST files are present. | | `flat_format` | Verbose | Whether the scan input is expected to use flat format. | Always in verbose mode. | | `extension_ranges` | Verbose | Enterprise extension ranges attached to the scan. | Enterprise builds only, when extension ranges exist. | | `metrics_per_partition` | Verbose | Per-partition scanner metrics collected by `PartitionMetrics`. | Verbose mode after partitions have metrics. | Each `files` entry contains: | Field | Meaning | | --- | --- | | `file_id` | Region file ID of the SST file. | | `time_range_start` | Inclusive start of the file time range, formatted as `value::unit`. | | `time_range_end` | Inclusive end of the file time range, formatted as `value::unit`. | | `rows` | Number of rows recorded in the file metadata. | | `size` | SST file size in bytes. | | `index_size` | Total index size in bytes recorded for the file. | Example verbose scanner display with synthetic values: ```text UnorderedScan: region=0(1, 0), {"partition_count":{"count":4, "mem_ranges":1, "files":2, "file_ranges":3}, "projection": ["host", "ts", "value"], "filters": ["host = Utf8(\"demo\")"], "dyn_filters": ["ts@1 < @runtime_filter"], "files": [{"file_id":"0(1, 0)/00000000-0000-0000-0000-000000000001","time_range_start":"1000::Millisecond","time_range_end":"2000::Millisecond","rows":1024,"size":65536,"index_size":4096}], "flat_format": true, "metrics_per_partition": [...]} ``` ### Per-Partition Metrics `metrics_per_partition` is a verbose-only list. Each item contains a partition number and that partition's metric snapshot. These values are not the same output layer as the aggregate `metrics=[...]` section, even when metric names overlap. Example with synthetic values: ```json { "metrics_per_partition": [ { "partition": 0, "metrics": { "prepare_scan_cost": "500µs", "build_reader_cost": "2ms", "scan_cost": "8ms", "yield_cost": "1ms", "total_cost": "12ms", "num_rows": 128, "num_batches": 4, "num_mem_ranges": 1, "num_file_ranges": 1, "build_parts_cost": "1ms", "sst_scan_cost": "6ms", "rg_total": 3, "rows_before_filter": 4096, "num_sst_record_batches": 2, "num_sst_batches": 2, "num_sst_rows": 96, "first_poll": "600µs", "convert_cost": "300µs", "rg_bloom_filtered": 1, "rows_bloom_filtered": 1024, "fetch_metrics": { "total_fetch_elapsed": "2ms", "page_cache_hit": 2, "cache_miss": 1 }, "metadata_cache_metrics": { "metadata_load_cost": "100µs", "mem_cache_hit": 1 }, "build_ranges_peak_mem_size": 2048, "num_peak_range_builders": 1, "stream_eof": true } } ] } ``` All three scanners use the same `PartitionMetrics` and `ScanMetricsSet` structure for verbose per-partition metrics. #### Timing and Output | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `prepare_scan_cost` | All | Elapsed time between query start and partition metric creation. | Always. | | `build_reader_cost` | All | Time spent building readers or merge readers in this partition. | Always. | | `scan_cost` | All | Time spent polling scanner inputs in this partition. | Always. | | `yield_cost` | All | Time spent after yielding batches to downstream operators in this partition. | Always. | | `convert_cost` | All | Time spent converting batches to Arrow record batches in this partition. | When conversion time is recorded. | | `total_cost` | All | Elapsed time from query start until the partition finishes, or until the metrics object is dropped. | Always. | | `first_poll` | All | Elapsed time from query start until the partition stream is first polled. | Always. | | `num_rows` | All | Rows returned by this partition. | Always. | | `num_batches` | All | Batches returned by this partition. | Always. | | `num_distributor_rows` | SeriesScan | Rows scanned by the series distributor. | Nonzero only. | | `num_distributor_batches` | SeriesScan | Batches scanned by the series distributor. | Nonzero only. | | `distributor_scan_cost` | SeriesScan | Time spent by the series distributor scanning input. | Nonzero only. | | `distributor_yield_cost` | SeriesScan | Time spent by the series distributor sending batches to partition channels. | Nonzero only. | | `distributor_divider_cost` | SeriesScan | Time spent splitting flat batches into series batches. | Nonzero only. | #### Ranges and SST | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `num_mem_ranges` | All | Memtable ranges scanned by this partition. | Always. | | `num_file_ranges` | All | SST file ranges scanned by this partition. | Always. | | `build_parts_cost` | All | Time spent building SST file ranges in this partition. | Always. | | `sst_scan_cost` | All | Time spent scanning SST readers. | Always. | | `rg_total` | All | Row groups considered before pruning. | Always. | | `num_sst_record_batches` | All | Arrow record batches read from SST readers. | Always. | | `num_sst_batches` | All | Batches decoded from SST readers. | Always. | | `num_sst_rows` | All | Rows decoded from SST readers. | Always. | #### Filters and Pruning | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `rg_fulltext_filtered` | All | Row groups filtered by fulltext index. | Nonzero only. | | `rg_inverted_filtered` | All | Row groups filtered by inverted index. | Nonzero only. | | `rg_minmax_filtered` | All | Row groups filtered by min-max pruning. | Nonzero only. | | `rg_bloom_filtered` | All | Row groups filtered by bloom filter index. | Nonzero only. | | `rg_vector_filtered` | All | Row groups filtered by vector index. | Nonzero only. | | `rows_before_filter` | All | Rows in candidate row groups before row-level filtering. | Always. | | `rows_fulltext_filtered` | All | Rows filtered by fulltext index. | Nonzero only. | | `rows_inverted_filtered` | All | Rows filtered by inverted index. | Nonzero only. | | `rows_bloom_filtered` | All | Rows filtered by bloom filter index. | Nonzero only. | | `rows_vector_filtered` | All | Rows filtered by vector index. | Nonzero only. | | `rows_vector_selected` | All | Rows selected by vector index search. | Nonzero only. | | `rows_precise_filtered` | All | Rows filtered by precise row-level filters. | Nonzero only. | | `pruner_cache_hit` | All | Pruner builder cache hits while building file ranges. | Nonzero only. | | `pruner_cache_miss` | All | Pruner builder cache misses while building file ranges. | Nonzero only. | | `pruner_prune_cost` | All | Time spent waiting for pruners to build file ranges. | Nonzero only. | #### Index Result Caches | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `fulltext_index_cache_hit` | All | Fulltext index result cache hits. | Nonzero only. | | `fulltext_index_cache_miss` | All | Fulltext index result cache misses. | Nonzero only. | | `inverted_index_cache_hit` | All | Inverted index result cache hits. | Nonzero only. | | `inverted_index_cache_miss` | All | Inverted index result cache misses. | Nonzero only. | | `bloom_filter_cache_hit` | All | Bloom filter index result cache hits. | Nonzero only. | | `bloom_filter_cache_miss` | All | Bloom filter index result cache misses. | Nonzero only. | | `minmax_cache_hit` | All | Min-max pruning cache hits. | Nonzero only. | | `minmax_cache_miss` | All | Min-max pruning cache misses. | Nonzero only. | #### Memtables | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `mem_scan_cost` | All | Time spent scanning memtables. | Nonzero only. | | `mem_rows` | All | Rows read from memtables. | Nonzero only. | | `mem_batches` | All | Batches read from memtables. | Nonzero only. | | `mem_series` | All | Series read from time-series memtables. | Nonzero only. | | `mem_prefilter_cost` | All | Time spent applying memtable prefilters. | Nonzero only. | | `mem_prefilter_rows_filtered` | All | Rows filtered by memtable prefilters. | Nonzero only. | #### SeriesScan Distributor | Metric | Meaning | When present | | --- | --- | --- | | `num_series_send_timeout` | Times the distributor timed out sending to a partition channel. | Nonzero only. | | `num_series_send_full` | Times a non-blocking send found a full partition channel. | Nonzero only. | #### Range Cache and Lifecycle | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `range_cache_size` | All | Bytes added to the range cache during the scan. | Nonzero only. | | `range_cache_hit` | All | Range cache lookup hits. | Nonzero only. | | `range_cache_miss` | All | Range cache lookup misses. | Nonzero only. | | `build_ranges_peak_mem_size` | All | Peak memory tracked while building file ranges. | Always. | | `num_peak_range_builders` | All | Peak number of active file range builders. | Always. | | `stream_eof` | All | Whether the partition stream reached EOF normally. | Always. | ### Nested Metrics Verbose per-partition output can include nested metric objects when the related work is recorded. | Metric | Scanner | Meaning | When present | | --- | --- | --- | --- | | `fetch_metrics` | All | Page and row-group fetch metrics from Parquet readers. | When fetch or prefilter work is recorded. | | `metadata_cache_metrics` | All | Parquet metadata cache metrics. | When metadata load time is recorded. | | `inverted_index_apply_metrics` | All | Elapsed time and cache/read metrics for inverted index appliers. | When inverted indexes are applied. | | `bloom_filter_apply_metrics` | All | Elapsed time and cache/read metrics for bloom filter index appliers. | When bloom filter indexes are applied. | | `fulltext_index_apply_metrics` | All | Elapsed time and cache/read metrics for fulltext index appliers. | When fulltext indexes are applied. | | `merge_metrics` | SeqScan, SeriesScan | Merge reader metrics. | When merge scan cost is recorded. | | `dedup_metrics` | SeqScan, SeriesScan | Deduplication metrics. | When dedup cost is recorded. | | `top_file_metrics` | All | Up to ten files with the largest accumulated `build_part_cost + build_reader_cost + scan_cost`. | When per-file metrics are collected in verbose mode. | `fetch_metrics` fields: | Field | Meaning | When present | | --- | --- | --- | | `total_fetch_elapsed` | Total elapsed time for fetching row groups. | Always when `fetch_metrics` is printed. | | `page_cache_hit` | Page cache hits. | Nonzero only. | | `write_cache_hit` | Write cache hits. | Nonzero only. | | `cache_miss` | Cache misses. | Nonzero only. | | `pages_to_fetch_mem` | Pages to fetch from memory cache. | Nonzero only. | | `page_size_to_fetch_mem` | Bytes to fetch from memory cache. | Nonzero only. | | `pages_to_fetch_write_cache` | Pages to fetch from write cache. | Nonzero only. | | `page_size_to_fetch_write_cache` | Bytes to fetch from write cache. | Nonzero only. | | `pages_to_fetch_store` | Pages to fetch from object store. | Nonzero only. | | `page_size_to_fetch_store` | Bytes to fetch from object store. | Nonzero only. | | `page_size_needed` | Bytes actually needed by the read. | Nonzero only. | | `write_cache_fetch_elapsed` | Elapsed time fetching from write cache. | Nonzero only. | | `store_fetch_elapsed` | Elapsed time fetching from object store. | Nonzero only. | | `prefilter_cost` | Elapsed time running row-group prefilters. | Nonzero only. | | `prefilter_filtered_rows` | Rows filtered by row-group prefilters. | Nonzero only. | `metadata_cache_metrics` fields: | Field | Meaning | When present | | --- | --- | --- | | `metadata_load_cost` | Time spent loading Parquet metadata. | Always when `metadata_cache_metrics` is printed. | | `mem_cache_hit` | Parquet metadata memory cache hits. | Nonzero only. | | `file_cache_hit` | Parquet metadata file cache hits. | Nonzero only. | | `cache_miss` | Parquet metadata cache misses. | Nonzero only. | | `num_reads` | Metadata read operations. | Nonzero only. | | `bytes_read` | Metadata bytes read from storage. | Nonzero only. | Index apply metric fields: | Parent metric | Field | Meaning | When present | | --- | --- | --- | --- | | `inverted_index_apply_metrics` | `apply_elapsed` | Time spent applying inverted indexes. | Always when parent is printed. | | `inverted_index_apply_metrics` | `blob_cache_miss` | Index blob cache misses. | Nonzero only. | | `inverted_index_apply_metrics` | `blob_read_bytes` | Bytes read for index blobs. | Nonzero only. | | `inverted_index_apply_metrics` | `inverted_index_read_metrics` | Nested inverted index read metrics. | Always when parent is printed. | | `bloom_filter_apply_metrics` | `apply_elapsed` | Time spent applying bloom filter indexes. | Always when parent is printed. | | `bloom_filter_apply_metrics` | `blob_cache_miss` | Index blob cache misses. | Nonzero only. | | `bloom_filter_apply_metrics` | `blob_read_bytes` | Bytes read for index blobs. | Nonzero only. | | `bloom_filter_apply_metrics` | `read_metrics` | Nested bloom filter read metrics. | Always when parent is printed. | | `fulltext_index_apply_metrics` | `apply_elapsed` | Time spent applying fulltext indexes. | Always when parent is printed. | | `fulltext_index_apply_metrics` | `blob_cache_miss` | Fulltext index blob cache misses. | Nonzero only. | | `fulltext_index_apply_metrics` | `dir_cache_hit` | Fulltext index directory cache hits. | Nonzero only. | | `fulltext_index_apply_metrics` | `dir_cache_miss` | Fulltext index directory cache misses. | Nonzero only. | | `fulltext_index_apply_metrics` | `dir_init_elapsed` | Time spent initializing fulltext index directory data. | Nonzero only. | | `fulltext_index_apply_metrics` | `bloom_filter_read_metrics` | Nested bloom filter read metrics used by the fulltext path. | Always when parent is printed. | Index read metric fields used by `inverted_index_read_metrics`, `read_metrics`, and `bloom_filter_read_metrics`: | Field | Meaning | When present | | --- | --- | --- | | `total_bytes` | Bytes read for index data. | Nonzero only. | | `cache_hit` | Index data cache hits. | Nonzero only. | | `total_ranges` | Ranges read for index data. | Nonzero only. | | `fetch_elapsed` | Elapsed time fetching index data. | Nonzero only. | | `cache_miss` | Index data cache misses. | Nonzero only. | `merge_metrics` fields: | Field | Meaning | When present | | --- | --- | --- | | `scan_cost` | Total scan cost of the merge reader. | Always when `merge_metrics` is printed. | | `init_cost` | Time spent initializing the merge reader. | Nonzero only. | | `num_fetch_by_batches` | Number of batch-oriented fetches from sources. | Nonzero only. | | `num_fetch_by_rows` | Number of row-oriented fetches from sources. | Nonzero only. | | `fetch_cost` | Time spent fetching batches from sources. | Nonzero only. | `dedup_metrics` fields: | Field | Meaning | When present | | --- | --- | --- | | `dedup_cost` | Time spent deduplicating rows. | Always when `dedup_metrics` is printed. | | `num_unselected_rows` | Rows removed by deduplication or delete filtering. | Nonzero only. | | `num_deleted_rows` | Deleted rows removed during deduplication. | Nonzero only. | Each `top_file_metrics` entry is keyed by file ID and can contain: | Field | Meaning | When present | | --- | --- | --- | | `build_part_cost` | Time spent building this file's ranges or parts. | Always for each printed file entry. | | `num_ranges` | Ranges read from this file. | Nonzero only. | | `num_rows` | Rows read from this file. | Nonzero only. | | `build_reader_cost` | Time spent building readers for this file. | Nonzero only. | | `scan_cost` | Time spent scanning this file. | Nonzero only. | ### Scanner Differences `SeqScan` scans ranges while preserving the ordering required by the read path. It may report `merge_metrics` when merge readers are used and `dedup_metrics` when deduplication removes older versions or deleted rows. `UnorderedScan` provides no output ordering guarantee. It uses the same per-partition metric structure as `SeqScan`, but downstream plan nodes may add sort operators when a query needs ordered output. `SeriesScan` returns rows grouped by series. It uses the same per-partition metric structure as the other scanners and can also report distributor metrics from the series distributor partition. --- ## Anomaly Detection Functions GreptimeDB provides a set of statistical anomaly-scoring **window functions** that compute a numeric score reflecting how anomalous each row is relative to its window of values. All three functions must be used with an `OVER` clause (window function syntax). :::tip These functions return `NULL` when the window does not contain enough valid (non-NULL) data points. A score of `0.0` means the value is not anomalous; a larger value indicates a stronger anomaly. When the spread (stddev / MAD / IQR) is zero but the current value deviates from the window center, the returned score is `+inf`, meaning the deviation is infinitely anomalous. ::: ## `anomaly_score_zscore` Computes a Z-Score-based anomaly score for each row in a window. **Formula:** `|x − mean| / stddev` **Minimum valid samples:** 2 (uses population stddev, i.e. dividing by n) ```sql anomaly_score_zscore(value) OVER (window_spec) ``` **Arguments:** - **value**: A numeric column or expression to evaluate. **Return type:** `DOUBLE` **Degenerate cases:** | Condition | Result | |---|---| | Fewer than 2 valid points in window | `NULL` | | `stddev = 0` and `value = mean` | `0.0` | | `stddev = 0` and `value ≠ mean` | `+inf` | | Normal case | Finite positive `DOUBLE` | **Example:** ```sql SELECT ts, val, anomaly_score_zscore(val) OVER ( ORDER BY ts ROWS BETWEEN 4 PRECEDING AND CURRENT ROW ) AS zscore FROM metrics ORDER BY ts; ``` ## `anomaly_score_mad` Computes a Median Absolute Deviation (MAD)-based anomaly score for each row in a window. MAD is more robust than Z-Score because it is not influenced by extreme outliers. **Formula:** `|x − median| / (MAD × 1.4826)` The constant 1.4826 is a consistency factor that makes the MAD-based score asymptotically equivalent to the Z-Score for normally distributed data. **Minimum valid samples:** 3 (with ≤ 2 samples, MAD is almost always 0, which yields spurious `+inf` scores) ```sql anomaly_score_mad(value) OVER (window_spec) ``` **Arguments:** - **value**: A numeric column or expression to evaluate. **Return type:** `DOUBLE` **Degenerate cases:** | Condition | Result | |---|---| | Fewer than 3 valid points in window | `NULL` | | `MAD = 0` and `value = median` | `0.0` | | `MAD = 0` and `value ≠ median` | `+inf` | | Normal case | Finite positive `DOUBLE` | **Example:** ```sql SELECT ts, val, anomaly_score_mad(val) OVER ( PARTITION BY host ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS mad_score FROM metrics ORDER BY host, ts; ``` ## `anomaly_score_iqr` Computes an IQR (Interquartile Range / Tukey Fences)-based anomaly score for each row in a window. The score measures the distance of the value beyond the lower fence (`Q1 − k × IQR`) or upper fence (`Q3 + k × IQR`). Values within the fences receive a score of `0.0`. **Formula:** - If `value < Q1 − k × IQR`: score = `(Q1 − k × IQR − value) / IQR` - If `value > Q3 + k × IQR`: score = `(value − Q3 − k × IQR) / IQR` - Otherwise: score = `0.0` **Minimum valid samples:** 3 (linear-interpolated Q1 ≠ Q3 is only possible at n ≥ 3) ```sql anomaly_score_iqr(value, k) OVER (window_spec) ``` **Arguments:** - **value**: A numeric column or expression to evaluate. - **k**: A non-negative `DOUBLE` multiplier for the IQR fences (e.g., `1.5` for standard Tukey fences, `3.0` for far-out fences). Returns `NULL` if `k < 0`. **Return type:** `DOUBLE` **Degenerate cases:** | Condition | Result | |---|---| | Fewer than 3 valid points in window | `NULL` | | `IQR = 0` and value is within fences | `0.0` | | `IQR = 0` and value is outside fences | `+inf` | | Normal case | Finite non-negative `DOUBLE` | **Example:** ```sql SELECT ts, val, anomaly_score_iqr(val, 1.5) OVER ( PARTITION BY host ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS iqr_score FROM metrics ORDER BY host, ts; ``` ## Full Usage Example This example creates a sample table, inserts time-series data with an injected outlier, and then uses all three anomaly functions together. ```sql CREATE TABLE sensor_data ( host STRING, val DOUBLE, ts TIMESTAMP TIME INDEX, PRIMARY KEY (host) ); INSERT INTO sensor_data VALUES ('web-1', 10.0, '2025-01-01 00:00:00'), ('web-1', 11.0, '2025-01-01 00:01:00'), ('web-1', 10.5, '2025-01-01 00:02:00'), ('web-1', 10.8, '2025-01-01 00:03:00'), ('web-1', 80.0, '2025-01-01 00:04:00'), -- outlier ('web-1', 10.3, '2025-01-01 00:05:00'), ('web-1', 11.2, '2025-01-01 00:06:00'); ``` Use a shared named window and round results for readability: ```sql SELECT ts, val, ROUND(anomaly_score_zscore(val) OVER w, 2) AS zscore, ROUND(anomaly_score_mad(val) OVER w, 2) AS mad, ROUND(anomaly_score_iqr(val, 1.5) OVER w, 2) AS iqr FROM sensor_data WINDOW w AS ( PARTITION BY host ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) ORDER BY ts; ``` Expected output — the outlier row (`val = 80.0`) produces significantly higher scores across all three metrics: ``` +---------------------+------+--------+--------+-------+ | ts | val | zscore | mad | iqr | +---------------------+------+--------+--------+-------+ | 2025-01-01 00:00:00 | 10 | NULL | NULL | NULL | | 2025-01-01 00:01:00 | 11 | 1 | NULL | NULL | | 2025-01-01 00:02:00 | 10.5 | 0 | 0 | 0 | | 2025-01-01 00:03:00 | 10.8 | 0.6 | 0.4 | 0 | | 2025-01-01 00:04:00 | 80 | 2 | 155.58 | 136.5 | | 2025-01-01 00:05:00 | 10.3 | 0.46 | 0.67 | 0 | | 2025-01-01 00:06:00 | 11.2 | 0.38 | 0.67 | 0 | +---------------------+------+--------+--------+-------+ ``` ### Filter Anomalous Rows You can wrap the window query in a subquery to keep only rows whose score exceeds a threshold: ```sql SELECT * FROM ( SELECT host, ts, val, ROUND(anomaly_score_mad(val) OVER ( PARTITION BY host ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ), 2) AS mad FROM sensor_data ) WHERE mad > 3.0 ORDER BY host, ts; ``` Expected output: ``` +-------+---------------------+------+--------+ | host | ts | val | mad | +-------+---------------------+------+--------+ | web-1 | 2025-01-01 00:04:00 | 80 | 155.58 | +-------+---------------------+------+--------+ ``` --- ## Approximate Functions GreptimeDB provides functions for approximate distinct counts and quantiles. :::warning These functions are experimental and may change in future releases. ::: ## Approximate Count Distinct (HLL) GreptimeDB implements [HyperLogLog](https://algo.inria.fr/flajolet/Publications/FlFuGaMe07.pdf) (HLL) for memory-efficient, fast approximate distinct counts. Three functions create a sketch, merge sketches, and read the approximate count. :::warning HLL returns an estimate rather than an exact count. Its relative standard error is approximately `1.04 / sqrt(m)`, where `m` is the number of registers. GreptimeDB uses 16,384 registers, corresponding to approximately 0.8125% relative standard error. ::: ### `hll` `hll(value)` coerces each value to `STRING` and aggregates it into a binary HLL state. The state can be stored in a `BINARY` column, merged with other states, or passed to `hll_count`. Persisting the state makes it possible to calculate later rollups without retaining or scanning every original value. ### `hll_merge` `hll_merge(hll_state)` aggregates binary states produced by [`hll`](#hll) into one state. Use it to combine sketches from different groups, time windows, or data sources. This supports staged aggregation: a query can create and store states at one granularity, then merge them into larger rollups later. ### `hll_count` `hll_count(hll_state)` returns the approximate distinct count from a state produced by `hll` or `hll_merge`. HLL states are binary intermediate values rather than human-readable counts, so apply `hll_count` when returning the estimate to a query. ### Full Usage Example This example calculates approximate distinct user counts by time window. It first creates compact states from raw access records, stores those states, and then reads or merges them for different reporting windows. Create `access_log` for source rows and `access_log_10s` for one binary HLL state per 10-second window. The `BINARY` state column preserves the sketch needed for later counts and rollups. ```sql CREATE TABLE access_log ( `url` STRING, user_id BIGINT, ts TIMESTAMP TIME INDEX, PRIMARY KEY (`url`, `user_id`) ); CREATE TABLE access_log_10s ( `url` STRING, time_window timestamp time INDEX, state BINARY, PRIMARY KEY (`url`) ); ``` Insert sample data into `access_log`: ```sql INSERT INTO access_log VALUES ("/dashboard", 1, "2025-03-04 00:00:00"), ("/dashboard", 1, "2025-03-04 00:00:01"), ("/dashboard", 2, "2025-03-04 00:00:05"), ("/dashboard", 2, "2025-03-04 00:00:10"), ("/dashboard", 2, "2025-03-04 00:00:13"), ("/dashboard", 4, "2025-03-04 00:00:15"), ("/not_found", 1, "2025-03-04 00:00:10"), ("/not_found", 3, "2025-03-04 00:00:11"), ("/not_found", 4, "2025-03-04 00:00:12"); ``` Group rows into 10-second windows and store one HLL state for each URL and window. `date_bin` assigns each row to a window, and `hll` aggregates the user IDs in that group: ```sql -- Use a 10-second windowed query to calculate the HyperLogLog states INSERT INTO access_log_10s SELECT `url`, date_bin("10s" :: INTERVAL, ts) AS time_window, hll(`user_id`) AS state FROM access_log GROUP BY `url`, time_window; -- results will be similar to this: -- Query OK, 3 rows affected (0.05 sec) ``` Read the approximate distinct count from each stored state. For very small groups, an exact count can be more appropriate because HLL is designed for large cardinalities and returns an estimate: ```sql -- use hll_count to query approximate data in access_log_10s, notice for small datasets, the results may not be very accurate. SELECT `url`, `time_window`, hll_count(state) FROM access_log_10s; -- results as follows: -- +------------+---------------------+---------------------------------+ -- | url | time_window | hll_count(access_log_10s.state) | -- +------------+---------------------+---------------------------------+ -- | /dashboard | 2025-03-04 00:00:00 | 2 | -- | /dashboard | 2025-03-04 00:00:10 | 2 | -- | /not_found | 2025-03-04 00:00:10 | 3 | -- +------------+---------------------+---------------------------------+ ``` Merge the 10-second states to calculate a distinct count for each one-minute window. Because HLL states are composable, this rollup does not need to scan the original access records again and is useful for trend analysis: ```sql -- aggregate the 10-second data to a 1-minute level by merging the HyperLogLog states using `hll_merge`. SELECT `url`, date_bin('1 minute' :: INTERVAL, `time_window`) AS time_window_1m, hll_count(hll_merge(state)) as uv_per_min FROM access_log_10s GROUP BY `url`, date_bin('1 minute' :: INTERVAL, `time_window`); -- results as follows: -- +------------+---------------------+------------+ -- | url | time_window_1m | uv_per_min | -- +------------+---------------------+------------+ -- | /dashboard | 2025-03-04 00:00:00 | 3 | -- | /not_found | 2025-03-04 00:00:00 | 3 | -- +------------+---------------------+------------+ ``` `hll_merge` returns another binary state. Apply `hll_count` to that state to obtain the estimate. The following diagram shows the state creation, count, and merge operations: ![HLL Usage Flowchart](/hll.svg) ## Approximate Quantile (UDDSketch) GreptimeDB implements [UDDSketch](https://arxiv.org/abs/2004.08604) with functions to create, merge, and query sketch states. :::warning UDDSketch provides fast approximate quantiles with bounded memory use. Its memory use and error depend on `bucket_num`, `error_rate`, and the input value range, as described below. ::: ### `uddsketch_state` `uddsketch_state(bucket_num, error_rate, value)` aggregates `DOUBLE` values into a binary state. The state is a compact summary of the value distribution, organized into logarithmic buckets rather than retaining every input value. - `bucket_num`: Maximum number of buckets in the sketch. - `error_rate`: Initial relative-error bound. - `value`: `DOUBLE` expression to aggregate. The state can be stored in a `BINARY` column, merged with `uddsketch_merge`, or queried with `uddsketch_calc`. Storing states is useful when later queries need quantiles at several time granularities. ### `uddsketch_merge` `uddsketch_merge(bucket_num, error_rate, udd_state)` aggregates binary UDDSketch states into one state. Use it to combine distributions from different groups, time windows, or data sources without rereading the original values. `bucket_num` and `error_rate` must match the parameters used to create the input states; otherwise, the merge fails. ### `uddsketch_calc` `uddsketch_calc(quantile, udd_state)` returns a quantile estimate from a state created by `uddsketch_state` or `uddsketch_merge`. It reads the state without changing it, so the same stored state can be used to calculate several quantiles. - `quantile`: Value from 0 through 1; for example, `0.99` requests the 99th percentile. - `udd_state`: Binary UDDSketch state. See [UDDSketch Full Usage Example](#uddsketch-full-usage-example) for an example that combines these functions. ### How to determine `bucket_num` and `error_rate` The `bucket_num` parameter sets the maximum number of internal buckets and therefore bounds the sketch's memory use. A larger value can represent a wider ratio between the minimum and maximum values before compaction. When the sketch reaches this limit, it merges buckets at one end of the value range and loses accuracy. The recommended value is `128`, which balances accuracy and memory use for most workloads. The `error_rate` sets the initial relative-error bound used to map values to buckets. A smaller value creates more granular buckets. If the value range requires more than `bucket_num` buckets, UDDSketch compacts buckets and the effective maximum error increases. Therefore, an `error_rate` of `0.01` is not an unconditional guarantee that every result remains within 1% of the exact value. These parameters trade memory for accuracy. A small `error_rate` requires enough buckets for the data's dynamic range. If `bucket_num` is too small, decreasing `error_rate` does not prevent compaction or the resulting increase in maximum error. ### UDDSketch Full Usage Example This example combines the three `uddsketch` functions to calculate approximate quantiles. It stores one state per five-second window, calculates p99 from each state, and then merges those states for a one-minute rollup. Create `percentile_base` for the raw data and `percentile_5s` for the UDDSketch states in each five-second window. The `percentile_state` column stores the binary sketch state so that later queries can calculate or merge quantiles without scanning the raw table. ```sql CREATE TABLE percentile_base ( `id` INT PRIMARY KEY, `value` DOUBLE, `ts` timestamp(0) time index ); CREATE TABLE percentile_5s ( `percentile_state` BINARY, `time_window` timestamp(0) time index ); ``` Insert some sample data into `percentile_base` : ```sql INSERT INTO percentile_base (`id`, `value`, `ts`) VALUES (1, 10.0, 1), (2, 20.0, 2), (3, 30.0, 3), (4, 40.0, 4), (5, 50.0, 5), (6, 60.0, 6), (7, 70.0, 7), (8, 80.0, 8), (9, 90.0, 9), (10, 100.0, 10); ``` Group rows into five-second windows and store one UDDSketch state for each window. `date_bin` assigns input values to a window, and `uddsketch_state` summarizes the distribution in that group: ```sql INSERT INTO percentile_5s SELECT uddsketch_state(128, 0.01, `value`) AS percentile_state, date_bin('5 seconds' :: INTERVAL, `ts`) AS time_window FROM percentile_base GROUP BY time_window; -- results will be similar to this: -- Query OK, 3 rows affected (0.05 sec) ``` Calculate p99 for each stored state. The quantile argument `0.99` asks for an estimate of the value below which approximately 99% of the observations fall: ```sql -- query percentile_5s to get the approximate 99th percentile SELECT time_window, uddsketch_calc(0.99, `percentile_state`) AS p99 FROM percentile_5s; -- results as follows: -- +---------------------+--------------------+ -- | time_window | p99 | -- +---------------------+--------------------+ -- | 1970-01-01 00:00:00 | 40.04777053326359 | -- | 1970-01-01 00:00:05 | 89.13032933635911 | -- | 1970-01-01 00:00:10 | 100.49456770856492 | -- +---------------------+--------------------+ ``` Merge the five-second states to calculate p99 for each one-minute window. `uddsketch_merge` combines the stored distributions, and the outer `uddsketch_calc` reads p99 from the merged state. This avoids rebuilding the one-minute distribution from raw rows: ```sql -- in addition, we can aggregate the 5-second data to a 1-minute level by merging the UDDSketch states using `uddsketch_merge`. SELECT date_bin('1 minute' :: INTERVAL, `time_window`) AS time_window_1m, uddsketch_calc(0.99, uddsketch_merge(128, 0.01, `percentile_state`)) AS p99 FROM percentile_5s GROUP BY time_window_1m; -- results as follows: -- +---------------------+--------------------+ -- | time_window_1m | p99 | -- +---------------------+--------------------+ -- | 1970-01-01 00:00:00 | 100.49456770856492 | -- +---------------------+--------------------+ ``` The following diagram shows the state creation, quantile calculation, and merge operations: ![UDDSketch Usage Flowchart](/udd.svg) --- ## DataFusion Functions This page is generated from the [Apache DataFusion](https://datafusion.apache.org/user-guide/sql/) project's documents: * [DataFusion Scalar Functions](#scalar-functions) * [DataFusion Aggregate Functions](#aggregate-functions) * [DataFusion Window Functions](#window-functions) * [DataFusion Special Functions](#special-functions) ## Scalar Functions ### Math Functions - [abs](#abs) - [acos](#acos) - [acosh](#acosh) - [asin](#asin) - [asinh](#asinh) - [atan](#atan) - [atan2](#atan2) - [atanh](#atanh) - [cbrt](#cbrt) - [ceil](#ceil) - [cos](#cos) - [cosh](#cosh) - [cot](#cot) - [degrees](#degrees) - [exp](#exp) - [factorial](#factorial) - [floor](#floor) - [gcd](#gcd) - [isnan](#isnan) - [iszero](#iszero) - [lcm](#lcm) - [ln](#ln) - [log](#log) - [log10](#log10) - [log2](#log2) - [nanvl](#nanvl) - [pi](#pi) - [pow](#pow) - [power](#power) - [radians](#radians) - [random](#random) - [round](#round) - [signum](#signum) - [sin](#sin) - [sinh](#sinh) - [sqrt](#sqrt) - [tan](#tan) - [tanh](#tanh) - [trunc](#trunc) ##### `abs` Returns the absolute value of a number. ```sql abs(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT abs(-5); +----------+ | abs(-5) | +----------+ | 5 | +----------+ ``` ##### `acos` Returns the arc cosine or inverse cosine of a number. ```sql acos(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT acos(1); +----------+ | acos(1) | +----------+ | 0.0 | +----------+ ``` ##### `acosh` Returns the area hyperbolic cosine or inverse hyperbolic cosine of a number. ```sql acosh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT acosh(2); +------------+ | acosh(2) | +------------+ | 1.31696 | +------------+ ``` ##### `asin` Returns the arc sine or inverse sine of a number. ```sql asin(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT asin(0.5); +------------+ | asin(0.5) | +------------+ | 0.5235988 | +------------+ ``` ##### `asinh` Returns the area hyperbolic sine or inverse hyperbolic sine of a number. ```sql asinh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT asinh(1); +------------+ | asinh(1) | +------------+ | 0.8813736 | +------------+ ``` ##### `atan` Returns the arc tangent or inverse tangent of a number. ```sql atan(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT atan(1); +-----------+ | atan(1) | +-----------+ | 0.7853982 | +-----------+ ``` ##### `atan2` Returns the arc tangent or inverse tangent of `expression_y / expression_x`. ```sql atan2(expression_y, expression_x) ``` ###### Arguments - **expression_y**: First numeric expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **expression_x**: Second numeric expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. ###### Example ```sql > SELECT atan2(1, 1); +------------+ | atan2(1,1) | +------------+ | 0.7853982 | +------------+ ``` ##### `atanh` Returns the area hyperbolic tangent or inverse hyperbolic tangent of a number. ```sql atanh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT atanh(0.5); +-------------+ | atanh(0.5) | +-------------+ | 0.5493061 | +-------------+ ``` ##### `cbrt` Returns the cube root of a number. ```sql cbrt(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT cbrt(27); +-----------+ | cbrt(27) | +-----------+ | 3.0 | +-----------+ ``` ##### `ceil` Returns the nearest integer greater than or equal to a number. ```sql ceil(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT ceil(3.14); +------------+ | ceil(3.14) | +------------+ | 4.0 | +------------+ ``` ##### `cos` Returns the cosine of a number. ```sql cos(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT cos(0); +--------+ | cos(0) | +--------+ | 1.0 | +--------+ ``` ##### `cosh` Returns the hyperbolic cosine of a number. ```sql cosh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT cosh(1); +-----------+ | cosh(1) | +-----------+ | 1.5430806 | +-----------+ ``` ##### `cot` Returns the cotangent of a number. ```sql cot(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT cot(1); +---------+ | cot(1) | +---------+ | 0.64209 | +---------+ ``` ##### `degrees` Converts radians to degrees. ```sql degrees(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT degrees(pi()); +------------+ | degrees(0) | +------------+ | 180.0 | +------------+ ``` ##### `exp` Returns the base-e exponential of a number. ```sql exp(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT exp(1); +---------+ | exp(1) | +---------+ | 2.71828 | +---------+ ``` ##### `factorial` Factorial. Returns 1 if value is less than 2. ```sql factorial(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT factorial(5); +---------------+ | factorial(5) | +---------------+ | 120 | +---------------+ ``` ##### `floor` Returns the nearest integer less than or equal to a number. ```sql floor(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT floor(3.14); +-------------+ | floor(3.14) | +-------------+ | 3.0 | +-------------+ ``` ##### `gcd` Returns the greatest common divisor of `expression_x` and `expression_y`. Returns 0 if both inputs are zero. ```sql gcd(expression_x, expression_y) ``` ###### Arguments - **expression_x**: First numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_y**: Second numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT gcd(48, 18); +------------+ | gcd(48,18) | +------------+ | 6 | +------------+ ``` ##### `isnan` Returns true if a given number is +NaN or -NaN otherwise returns false. ```sql isnan(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT isnan(1); +----------+ | isnan(1) | +----------+ | false | +----------+ ``` ##### `iszero` Returns true if a given number is +0.0 or -0.0 otherwise returns false. ```sql iszero(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT iszero(0); +------------+ | iszero(0) | +------------+ | true | +------------+ ``` ##### `lcm` Returns the least common multiple of `expression_x` and `expression_y`. Returns 0 if either input is zero. ```sql lcm(expression_x, expression_y) ``` ###### Arguments - **expression_x**: First numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_y**: Second numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT lcm(4, 5); +----------+ | lcm(4,5) | +----------+ | 20 | +----------+ ``` ##### `ln` Returns the natural logarithm of a number. ```sql ln(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT ln(2.71828); +-------------+ | ln(2.71828) | +-------------+ | 1.0 | +-------------+ ``` ##### `log` Returns the base-x logarithm of a number. Can either provide a specified base, or if omitted then takes the base-10 of a number. ```sql log(base, numeric_expression) log(numeric_expression) ``` ###### Arguments - **base**: Base numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT log(10); +---------+ | log(10) | +---------+ | 1.0 | +---------+ ``` ##### `log10` Returns the base-10 logarithm of a number. ```sql log10(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT log10(100); +-------------+ | log10(100) | +-------------+ | 2.0 | +-------------+ ``` ##### `log2` Returns the base-2 logarithm of a number. ```sql log2(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT log2(8); +-----------+ | log2(8) | +-----------+ | 3.0 | +-----------+ ``` ##### `nanvl` Returns the first argument if it's not _NaN_. Returns the second argument otherwise. ```sql nanvl(expression_x, expression_y) ``` ###### Arguments - **expression_x**: Numeric expression to return if it's not _NaN_. Can be a constant, column, or function, and any combination of arithmetic operators. - **expression_y**: Numeric expression to return if the first expression is _NaN_. Can be a constant, column, or function, and any combination of arithmetic operators. ###### Example ```sql > SELECT nanvl(0, 5); +------------+ | nanvl(0,5) | +------------+ | 0 | +------------+ ``` ##### `pi` Returns an approximate value of π. ```sql pi() ``` ##### `pow` _Alias of [power](#power)._ ##### `power` Returns a base expression raised to the power of an exponent. ```sql power(base, exponent) ``` ###### Arguments - **base**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **exponent**: Exponent numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT power(2, 3); +-------------+ | power(2,3) | +-------------+ | 8 | +-------------+ ``` ###### Aliases - pow ##### `radians` Converts degrees to radians. ```sql radians(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT radians(180); +----------------+ | radians(180) | +----------------+ | 3.14159265359 | +----------------+ ``` ##### `random` Returns a random float value in the range [0, 1). The random seed is unique to each row. ```sql random() ``` ###### Example ```sql > SELECT random(); +------------------+ | random() | +------------------+ | 0.7389238902938 | +------------------+ ``` ##### `round` Rounds a number to the nearest integer. ```sql round(numeric_expression[, decimal_places]) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **decimal_places**: Optional. The number of decimal places to round to. Defaults to 0. ###### Example ```sql > SELECT round(3.14159); +--------------+ | round(3.14159)| +--------------+ | 3.0 | +--------------+ ``` ##### `signum` Returns the sign of a number. Negative numbers return `-1`. Zero and positive numbers return `1`. ```sql signum(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT signum(-42); +-------------+ | signum(-42) | +-------------+ | -1 | +-------------+ ``` ##### `sin` Returns the sine of a number. ```sql sin(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT sin(0); +----------+ | sin(0) | +----------+ | 0.0 | +----------+ ``` ##### `sinh` Returns the hyperbolic sine of a number. ```sql sinh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT sinh(1); +-----------+ | sinh(1) | +-----------+ | 1.1752012 | +-----------+ ``` ##### `sqrt` Returns the square root of a number. ```sql sqrt(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ##### `tan` Returns the tangent of a number. ```sql tan(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT tan(pi()/4); +--------------+ | tan(PI()/4) | +--------------+ | 1.0 | +--------------+ ``` ##### `tanh` Returns the hyperbolic tangent of a number. ```sql tanh(numeric_expression) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT tanh(20); +----------+ | tanh(20) | +----------+ | 1.0 | +----------+ ``` ##### `trunc` Truncates a number to a whole number or truncated to the specified decimal places. ```sql trunc(numeric_expression[, decimal_places]) ``` ###### Arguments - **numeric_expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. - **decimal_places**: Optional. The number of decimal places to truncate to. Defaults to 0 (truncate to a whole number). If `decimal_places` is a positive integer, truncates digits to the right of the decimal point. If `decimal_places` is a negative integer, replaces digits to the left of the decimal point with `0`. ###### Example ```sql > SELECT trunc(42.738); +----------------+ | trunc(42.738) | +----------------+ | 42 | +----------------+ ``` ### Conditional Functions - [coalesce](#coalesce) - [greatest](#greatest) - [ifnull](#ifnull) - [least](#least) - [nullif](#nullif) - [nvl](#nvl) - [nvl2](#nvl2) ##### `coalesce` Returns the first of its arguments that is not _null_. Returns _null_ if all arguments are _null_. This function is often used to substitute a default value for _null_ values. ```sql coalesce(expression1[, ..., expression_n]) ``` ###### Arguments - **expression1, expression_n**: Expression to use if previous expressions are _null_. Can be a constant, column, or function, and any combination of arithmetic operators. Pass as many expression arguments as necessary. ###### Example ```sql > select coalesce(null, null, 'datafusion'); +----------------------------------------+ | coalesce(NULL,NULL,Utf8("datafusion")) | +----------------------------------------+ | datafusion | +----------------------------------------+ ``` ##### `greatest` Returns the greatest value in a list of expressions. Returns _null_ if all expressions are _null_. ```sql greatest(expression1[, ..., expression_n]) ``` ###### Arguments - **expression1, expression_n**: Expressions to compare and return the greatest value.. Can be a constant, column, or function, and any combination of arithmetic operators. Pass as many expression arguments as necessary. ###### Example ```sql > select greatest(4, 7, 5); +---------------------------+ | greatest(4,7,5) | +---------------------------+ | 7 | +---------------------------+ ``` ##### `ifnull` _Alias of [nvl](#nvl)._ ##### `least` Returns the smallest value in a list of expressions. Returns _null_ if all expressions are _null_. ```sql least(expression1[, ..., expression_n]) ``` ###### Arguments - **expression1, expression_n**: Expressions to compare and return the smallest value. Can be a constant, column, or function, and any combination of arithmetic operators. Pass as many expression arguments as necessary. ###### Example ```sql > select least(4, 7, 5); +---------------------------+ | least(4,7,5) | +---------------------------+ | 4 | +---------------------------+ ``` ##### `nullif` Returns _null_ if _expression1_ equals _expression2_; otherwise it returns _expression1_. This can be used to perform the inverse operation of [`coalesce`](#coalesce). ```sql nullif(expression1, expression2) ``` ###### Arguments - **expression1**: Expression to compare and return if equal to expression2. Can be a constant, column, or function, and any combination of operators. - **expression2**: Expression to compare to expression1. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select nullif('datafusion', 'data'); +-----------------------------------------+ | nullif(Utf8("datafusion"),Utf8("data")) | +-----------------------------------------+ | datafusion | +-----------------------------------------+ > select nullif('datafusion', 'datafusion'); +-----------------------------------------------+ | nullif(Utf8("datafusion"),Utf8("datafusion")) | +-----------------------------------------------+ | | +-----------------------------------------------+ ``` ##### `nvl` Returns _expression2_ if _expression1_ is NULL otherwise it returns _expression1_ and _expression2_ is not evaluated. This function can be used to substitute a default value for NULL values. ```sql nvl(expression1, expression2) ``` ###### Arguments - **expression1**: Expression to return if not null. Can be a constant, column, or function, and any combination of operators. - **expression2**: Expression to return if expr1 is null. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select nvl(null, 'a'); +---------------------+ | nvl(NULL,Utf8("a")) | +---------------------+ | a | +---------------------+\ > select nvl('b', 'a'); +--------------------------+ | nvl(Utf8("b"),Utf8("a")) | +--------------------------+ | b | +--------------------------+ ``` ###### Aliases - ifnull ##### `nvl2` Returns _expression2_ if _expression1_ is not NULL; otherwise it returns _expression3_. ```sql nvl2(expression1, expression2, expression3) ``` ###### Arguments - **expression1**: Expression to test for null. Can be a constant, column, or function, and any combination of operators. - **expression2**: Expression to return if expr1 is not null. Can be a constant, column, or function, and any combination of operators. - **expression3**: Expression to return if expr1 is null. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select nvl2(null, 'a', 'b'); +--------------------------------+ | nvl2(NULL,Utf8("a"),Utf8("b")) | +--------------------------------+ | b | +--------------------------------+ > select nvl2('data', 'a', 'b'); +----------------------------------------+ | nvl2(Utf8("data"),Utf8("a"),Utf8("b")) | +----------------------------------------+ | a | +----------------------------------------+ ``` ### String Functions - [ascii](#ascii) - [bit_length](#bit_length) - [btrim](#btrim) - [char_length](#char_length) - [character_length](#character_length) - [chr](#chr) - [concat](#concat) - [concat_ws](#concat_ws) - [contains](#contains) - [ends_with](#ends_with) - [find_in_set](#find_in_set) - [initcap](#initcap) - [instr](#instr) - [left](#left) - [length](#length) - [levenshtein](#levenshtein) - [lower](#lower) - [lpad](#lpad) - [ltrim](#ltrim) - [octet_length](#octet_length) - [overlay](#overlay) - [position](#position) - [repeat](#repeat) - [replace](#replace) - [reverse](#reverse) - [right](#right) - [rpad](#rpad) - [rtrim](#rtrim) - [split_part](#split_part) - [starts_with](#starts_with) - [strpos](#strpos) - [substr](#substr) - [substr_index](#substr_index) - [substring](#substring) - [substring_index](#substring_index) - [to_hex](#to_hex) - [translate](#translate) - [trim](#trim) - [upper](#upper) - [uuid](#uuid) ##### `ascii` Returns the first Unicode scalar value of a string. ```sql ascii(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select ascii('abc'); +--------------------+ | ascii(Utf8("abc")) | +--------------------+ | 97 | +--------------------+ > select ascii('🚀'); +-------------------+ | ascii(Utf8("🚀")) | +-------------------+ | 128640 | +-------------------+ ``` **Related functions**: - [chr](#chr) ##### `bit_length` Returns the bit length of a string. ```sql bit_length(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select bit_length('datafusion'); +--------------------------------+ | bit_length(Utf8("datafusion")) | +--------------------------------+ | 80 | +--------------------------------+ ``` **Related functions**: - [length](#length) - [octet_length](#octet_length) ##### `btrim` Trims the specified trim string from the start and end of a string. If no trim string is provided, all spaces are removed from the start and end of the input string. ```sql btrim(str[, trim_str]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **trim_str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. _Default is a space._ ###### Example ```sql > select btrim('__datafusion____', '_'); +-------------------------------------------+ | btrim(Utf8("__datafusion____"),Utf8("_")) | +-------------------------------------------+ | datafusion | +-------------------------------------------+ ``` ###### Alternative Syntax ```sql trim(BOTH trim_str FROM str) ``` ```sql trim(trim_str FROM str) ``` ###### Aliases - trim **Related functions**: - [ltrim](#ltrim) - [rtrim](#rtrim) ##### `char_length` _Alias of [character_length](#character_length)._ ##### `character_length` Returns the number of characters in a string. ```sql character_length(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select character_length('Ångström'); +------------------------------------+ | character_length(Utf8("Ångström")) | +------------------------------------+ | 8 | +------------------------------------+ ``` ###### Aliases - length - char_length **Related functions**: - [bit_length](#bit_length) - [octet_length](#octet_length) ##### `chr` Returns a string containing the character with the specified Unicode scalar value. ```sql chr(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select chr(128640); +--------------------+ | chr(Int64(128640)) | +--------------------+ | 🚀 | +--------------------+ ``` **Related functions**: - [ascii](#ascii) ##### `concat` Concatenates multiple strings together. ```sql concat(str[, ..., str_n]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **str_n**: Subsequent string expressions to concatenate. ###### Example ```sql > select concat('data', 'f', 'us', 'ion'); +-------------------------------------------------------+ | concat(Utf8("data"),Utf8("f"),Utf8("us"),Utf8("ion")) | +-------------------------------------------------------+ | datafusion | +-------------------------------------------------------+ ``` **Related functions**: - [concat_ws](#concat_ws) ##### `concat_ws` Concatenates multiple strings together with a specified separator. ```sql concat_ws(separator, str[, ..., str_n]) ``` ###### Arguments - **separator**: Separator to insert between concatenated strings. - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **str_n**: Subsequent string expressions to concatenate. ###### Example ```sql > select concat_ws('_', 'data', 'fusion'); +--------------------------------------------------+ | concat_ws(Utf8("_"),Utf8("data"),Utf8("fusion")) | +--------------------------------------------------+ | data_fusion | +--------------------------------------------------+ ``` **Related functions**: - [concat](#concat) ##### `contains` Return true if search_str is found within string (case-sensitive). ```sql contains(str, search_str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **search_str**: The string to search for in str. ###### Example ```sql > select contains('the quick brown fox', 'row'); +---------------------------------------------------+ | contains(Utf8("the quick brown fox"),Utf8("row")) | +---------------------------------------------------+ | true | +---------------------------------------------------+ ``` ##### `ends_with` Tests if a string ends with a substring. ```sql ends_with(str, substr) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **substr**: Substring to test for. ###### Example ```sql > select ends_with('datafusion', 'soin'); +--------------------------------------------+ | ends_with(Utf8("datafusion"),Utf8("soin")) | +--------------------------------------------+ | false | +--------------------------------------------+ > select ends_with('datafusion', 'sion'); +--------------------------------------------+ | ends_with(Utf8("datafusion"),Utf8("sion")) | +--------------------------------------------+ | true | +--------------------------------------------+ ``` ##### `find_in_set` Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings. ```sql find_in_set(str, strlist) ``` ###### Arguments - **str**: String expression to find in strlist. - **strlist**: A string list is a string composed of substrings separated by , characters. ###### Example ```sql > select find_in_set('b', 'a,b,c,d'); +----------------------------------------+ | find_in_set(Utf8("b"),Utf8("a,b,c,d")) | +----------------------------------------+ | 2 | +----------------------------------------+ ``` ##### `initcap` Capitalizes the first character in each word in the input string. Words are delimited by non-alphanumeric characters. ```sql initcap(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select initcap('apache datafusion'); +------------------------------------+ | initcap(Utf8("apache datafusion")) | +------------------------------------+ | Apache Datafusion | +------------------------------------+ ``` **Related functions**: - [lower](#lower) - [upper](#upper) ##### `instr` _Alias of [strpos](#strpos)._ ##### `left` Returns a specified number of characters from the left side of a string. ```sql left(str, n) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **n**: Number of characters to return. ###### Example ```sql > select left('datafusion', 4); +-----------------------------------+ | left(Utf8("datafusion"),Int64(4)) | +-----------------------------------+ | data | +-----------------------------------+ ``` **Related functions**: - [right](#right) ##### `length` _Alias of [character_length](#character_length)._ ##### `levenshtein` Returns the [`Levenshtein distance`](https://en.wikipedia.org/wiki/Levenshtein_distance) between the two given strings. ```sql levenshtein(str1, str2) ``` ###### Arguments - **str1**: String expression to compute Levenshtein distance with str2. - **str2**: String expression to compute Levenshtein distance with str1. ###### Example ```sql > select levenshtein('kitten', 'sitting'); +---------------------------------------------+ | levenshtein(Utf8("kitten"),Utf8("sitting")) | +---------------------------------------------+ | 3 | +---------------------------------------------+ ``` ##### `lower` Converts a string to lower-case. ```sql lower(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select lower('Ångström'); +-------------------------+ | lower(Utf8("Ångström")) | +-------------------------+ | ångström | +-------------------------+ ``` **Related functions**: - [initcap](#initcap) - [upper](#upper) ##### `lpad` Pads the left side of a string with another string to a specified string length. ```sql lpad(str, n[, padding_str]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **n**: String length to pad to. If the input string is longer than this length, it is truncated (on the right). - **padding_str**: Optional string expression to pad with. Can be a constant, column, or function, and any combination of string operators. _Default is a space._ ###### Example ```sql > select lpad('Dolly', 10, 'hello'); +---------------------------------------------+ | lpad(Utf8("Dolly"),Int64(10),Utf8("hello")) | +---------------------------------------------+ | helloDolly | +---------------------------------------------+ ``` **Related functions**: - [rpad](#rpad) ##### `ltrim` Trims the specified trim string from the beginning of a string. If no trim string is provided, spaces are removed from the start of the input string. ```sql ltrim(str[, trim_str]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **trim_str**: String expression to trim from the beginning of the input string. Can be a constant, column, or function, and any combination of arithmetic operators. _Default is a space._ ###### Example ```sql > select ltrim(' datafusion '); +-------------------------------+ | ltrim(Utf8(" datafusion ")) | +-------------------------------+ | datafusion | +-------------------------------+ > select ltrim('___datafusion___', '_'); +-------------------------------------------+ | ltrim(Utf8("___datafusion___"),Utf8("_")) | +-------------------------------------------+ | datafusion___ | +-------------------------------------------+ ``` ###### Alternative Syntax ```sql trim(LEADING trim_str FROM str) ``` **Related functions**: - [btrim](#btrim) - [rtrim](#rtrim) ##### `octet_length` Returns the length of a string in bytes. ```sql octet_length(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select octet_length('Ångström'); +--------------------------------+ | octet_length(Utf8("Ångström")) | +--------------------------------+ | 10 | +--------------------------------+ ``` **Related functions**: - [bit_length](#bit_length) - [length](#length) ##### `overlay` Returns the string which is replaced by another string from the specified position and specified count length. ```sql overlay(str PLACING substr FROM pos [FOR count]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **substr**: Substring to replace in str. - **pos**: The start position to start the replace in str. - **count**: The count of characters to be replaced from start position of str. If not specified, will use substr length instead. ###### Example ```sql > select overlay('Txxxxas' placing 'hom' from 2 for 4); +--------------------------------------------------------+ | overlay(Utf8("Txxxxas"),Utf8("hom"),Int64(2),Int64(4)) | +--------------------------------------------------------+ | Thomas | +--------------------------------------------------------+ ``` ##### `position` _Alias of [strpos](#strpos)._ ##### `repeat` Returns a string with an input string repeated a specified number. ```sql repeat(str, n) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **n**: Number of times to repeat the input string. ###### Example ```sql > select repeat('data', 3); +-------------------------------+ | repeat(Utf8("data"),Int64(3)) | +-------------------------------+ | datadatadata | +-------------------------------+ ``` ##### `replace` Replaces all occurrences of a specified substring in a string with a new substring. ```sql replace(str, substr, replacement) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **substr**: Substring expression to replace in the input string. Substring expression to operate on. Can be a constant, column, or function, and any combination of operators. - **replacement**: Replacement substring expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select replace('ABabbaBA', 'ab', 'cd'); +-------------------------------------------------+ | replace(Utf8("ABabbaBA"),Utf8("ab"),Utf8("cd")) | +-------------------------------------------------+ | ABcdbaBA | +-------------------------------------------------+ ``` ##### `reverse` Reverses the character order of a string. ```sql reverse(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select reverse('datafusion'); +-----------------------------+ | reverse(Utf8("datafusion")) | +-----------------------------+ | noisufatad | +-----------------------------+ ``` ##### `right` Returns a specified number of characters from the right side of a string. ```sql right(str, n) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **n**: Number of characters to return. ###### Example ```sql > select right('datafusion', 6); +------------------------------------+ | right(Utf8("datafusion"),Int64(6)) | +------------------------------------+ | fusion | +------------------------------------+ ``` **Related functions**: - [left](#left) ##### `rpad` Pads the right side of a string with another string to a specified string length. ```sql rpad(str, n[, padding_str]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **n**: String length to pad to. If the input string is longer than this length, it is truncated. - **padding_str**: String expression to pad with. Can be a constant, column, or function, and any combination of string operators. _Default is a space._ ###### Example ```sql > select rpad('datafusion', 20, '_-'); +-----------------------------------------------+ | rpad(Utf8("datafusion"),Int64(20),Utf8("_-")) | +-----------------------------------------------+ | datafusion_-_-_-_-_- | +-----------------------------------------------+ ``` **Related functions**: - [lpad](#lpad) ##### `rtrim` Trims the specified trim string from the end of a string. If no trim string is provided, all spaces are removed from the end of the input string. ```sql rtrim(str[, trim_str]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **trim_str**: String expression to trim from the end of the input string. Can be a constant, column, or function, and any combination of arithmetic operators. _Default is a space._ ###### Example ```sql > select rtrim(' datafusion '); +-------------------------------+ | rtrim(Utf8(" datafusion ")) | +-------------------------------+ | datafusion | +-------------------------------+ > select rtrim('___datafusion___', '_'); +-------------------------------------------+ | rtrim(Utf8("___datafusion___"),Utf8("_")) | +-------------------------------------------+ | ___datafusion | +-------------------------------------------+ ``` ###### Alternative Syntax ```sql trim(TRAILING trim_str FROM str) ``` **Related functions**: - [btrim](#btrim) - [ltrim](#ltrim) ##### `split_part` Splits a string based on a specified delimiter and returns the substring in the specified position. ```sql split_part(str, delimiter, pos) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **delimiter**: String or character to split on. - **pos**: Position of the part to return (counting from 1). Negative values count backward from the end of the string. ###### Example ```sql > select split_part('1.2.3.4.5', '.', 3); +--------------------------------------------------+ | split_part(Utf8("1.2.3.4.5"),Utf8("."),Int64(3)) | +--------------------------------------------------+ | 3 | +--------------------------------------------------+ ``` ##### `starts_with` Tests if a string starts with a substring. ```sql starts_with(str, substr) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **substr**: Substring to test for. ###### Example ```sql > select starts_with('datafusion','data'); +----------------------------------------------+ | starts_with(Utf8("datafusion"),Utf8("data")) | +----------------------------------------------+ | true | +----------------------------------------------+ ``` ##### `strpos` Returns the starting position of a specified substring in a string. Positions begin at 1. If the substring does not exist in the string, the function returns 0. ```sql strpos(str, substr) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **substr**: Substring expression to search for. ###### Example ```sql > select strpos('datafusion', 'fus'); +----------------------------------------+ | strpos(Utf8("datafusion"),Utf8("fus")) | +----------------------------------------+ | 5 | +----------------------------------------+ ``` ###### Alternative Syntax ```sql position(substr in origstr) ``` ###### Aliases - instr - position ##### `substr` Extracts a substring of a specified number of characters from a specific starting position in a string. ```sql substr(str, start_pos[, length]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **start_pos**: Character position to start the substring at. The first character in the string has a position of 1. If the start position is less than 1, it is treated as if it is before the start of the string and the (absolute) number of characters before position 1 is subtracted from `length` (if given). For example, `substr('abc', -3, 6)` returns `'ab'`. - **length**: Number of characters to extract. If not specified, returns the rest of the string after the start position. ###### Example ```sql > select substr('datafusion', 5, 3); +----------------------------------------------+ | substr(Utf8("datafusion"),Int64(5),Int64(3)) | +----------------------------------------------+ | fus | +----------------------------------------------+ ``` ###### Alternative Syntax ```sql substring(str from start_pos for length) ``` ###### Aliases - substring ##### `substr_index` Returns the substring from str before count occurrences of the delimiter delim. If count is positive, everything to the left of the final delimiter (counting from the left) is returned. If count is negative, everything to the right of the final delimiter (counting from the right) is returned. ```sql substr_index(str, delim, count) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **delim**: The string to find in str to split str. - **count**: The number of times to search for the delimiter. Can be either a positive or negative number. ###### Example ```sql > select substr_index('www.apache.org', '.', 1); +---------------------------------------------------------+ | substr_index(Utf8("www.apache.org"),Utf8("."),Int64(1)) | +---------------------------------------------------------+ | www | +---------------------------------------------------------+ > select substr_index('www.apache.org', '.', -1); +----------------------------------------------------------+ | substr_index(Utf8("www.apache.org"),Utf8("."),Int64(-1)) | +----------------------------------------------------------+ | org | +----------------------------------------------------------+ ``` ###### Aliases - substring_index ##### `substring` _Alias of [substr](#substr)._ ##### `substring_index` _Alias of [substr_index](#substr_index)._ ##### `to_hex` Converts an integer to a hexadecimal string. ```sql to_hex(int) ``` ###### Arguments - **int**: Integer expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select to_hex(12345689); +-------------------------+ | to_hex(Int64(12345689)) | +-------------------------+ | bc6159 | +-------------------------+ ``` ##### `translate` Performs character-wise substitution based on a mapping. ```sql translate(str, from, to) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **from**: The characters to be replaced. - **to**: The characters to replace them with. Each character in **from** that is found in **str** is replaced by the character at the same index in **to**. Any characters in **from** that don't have a corresponding character in **to** are removed. If a character appears more than once in **from**, the first occurrence determines the mapping. ###### Example ```sql > select translate('twice', 'wic', 'her'); +--------------------------------------------------+ | translate(Utf8("twice"),Utf8("wic"),Utf8("her")) | +--------------------------------------------------+ | there | +--------------------------------------------------+ ``` ##### `trim` _Alias of [btrim](#btrim)._ ##### `upper` Converts a string to upper-case. ```sql upper(str) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select upper('dataFusion'); +---------------------------+ | upper(Utf8("dataFusion")) | +---------------------------+ | DATAFUSION | +---------------------------+ ``` **Related functions**: - [initcap](#initcap) - [lower](#lower) ##### `uuid` Returns [`UUID v4`](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_%28random%29) string value which is unique per row. ```sql uuid() ``` ###### Example ```sql > select uuid(); +--------------------------------------+ | uuid() | +--------------------------------------+ | 6ec17ef8-1934-41cc-8d59-d0c8f9eea1f0 | +--------------------------------------+ ``` ### Binary String Functions - [decode](#decode) - [encode](#encode) ##### `decode` Decode binary data from textual representation in string. ```sql decode(expression, format) ``` ###### Arguments - **expression**: Expression containing encoded string data - **format**: Same arguments as [encode](#encode) **Related functions**: - [encode](#encode) ##### `encode` Encode binary data into a textual representation. ```sql encode(expression, format) ``` ###### Arguments - **expression**: Expression containing string or binary data - **format**: Supported formats are: `base64`, `base64pad`, `hex` **Related functions**: - [decode](#decode) ### Regular Expression Functions Apache DataFusion uses a [PCRE-like](https://en.wikibooks.org/wiki/Regular_Expressions/Perl-Compatible_Regular_Expressions) regular expression [syntax](https://docs.rs/regex/latest/regex/#syntax) (minus support for several features including look-around and backreferences). The following regular expression functions are supported: - [regexp_count](#regexp_count) - [regexp_instr](#regexp_instr) - [regexp_like](#regexp_like) - [regexp_match](#regexp_match) - [regexp_replace](#regexp_replace) ##### `regexp_count` Returns the number of matches that a [regular expression](https://docs.rs/regex/latest/regex/#syntax) has in a string. ```sql regexp_count(str, regexp[, start, flags]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to operate on. Can be a constant, column, or function, and any combination of operators. - **start**: - **start**: Optional start position (the first position is 1) to search for the regular expression. Can be a constant, column, or function. - **flags**: Optional regular expression flags that control the behavior of the regular expression. The following flags are supported: - **i**: case-insensitive: letters match both upper and lower case - **m**: multi-line mode: ^ and $ match begin/end of line - **s**: allow . to match \n - **R**: enables CRLF mode: when multi-line mode is enabled, \r\n is used - **U**: swap the meaning of x* and x*? ###### Example ```sql > select regexp_count('abcAbAbc', 'abc', 2, 'i'); +---------------------------------------------------------------+ | regexp_count(Utf8("abcAbAbc"),Utf8("abc"),Int64(2),Utf8("i")) | +---------------------------------------------------------------+ | 1 | +---------------------------------------------------------------+ ``` ##### `regexp_instr` Returns the position in a string where the specified occurrence of a POSIX regular expression is located. ```sql regexp_instr(str, regexp[, start[, N[, flags[, subexpr]]]]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to operate on. Can be a constant, column, or function, and any combination of operators. - **start**: - **start**: Optional start position (the first position is 1) to search for the regular expression. Can be a constant, column, or function. Defaults to 1 - **N**: - **N**: Optional The N-th occurrence of pattern to find. Defaults to 1 (first match). Can be a constant, column, or function. - **flags**: Optional regular expression flags that control the behavior of the regular expression. The following flags are supported: - **i**: case-insensitive: letters match both upper and lower case - **m**: multi-line mode: ^ and $ match begin/end of line - **s**: allow . to match \n - **R**: enables CRLF mode: when multi-line mode is enabled, \r\n is used - **U**: swap the meaning of x* and x*? - **subexpr**: Optional Specifies which capture group (subexpression) to return the position for. Defaults to 0, which returns the position of the entire match. ###### Example ```sql > SELECT regexp_instr('ABCDEF', 'C(.)(..)'); +---------------------------------------------------------------+ | regexp_instr(Utf8("ABCDEF"),Utf8("C(.)(..)")) | +---------------------------------------------------------------+ | 3 | +---------------------------------------------------------------+ ``` ##### `regexp_like` Returns true if a [regular expression](https://docs.rs/regex/latest/regex/#syntax) has at least one match in a string, false otherwise. ```sql regexp_like(str, regexp[, flags]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to operate on. Can be a constant, column, or function, and any combination of operators. - **flags**: Optional regular expression flags that control the behavior of the regular expression. The following flags are supported: - **i**: case-insensitive: letters match both upper and lower case - **m**: multi-line mode: ^ and $ match begin/end of line - **s**: allow . to match \n - **R**: enables CRLF mode: when multi-line mode is enabled, \r\n is used - **U**: swap the meaning of x* and x*? ###### Example ```sql select regexp_like('Köln', '[a-zA-Z]ö[a-zA-Z]{2}'); +--------------------------------------------------------+ | regexp_like(Utf8("Köln"),Utf8("[a-zA-Z]ö[a-zA-Z]{2}")) | +--------------------------------------------------------+ | true | +--------------------------------------------------------+ SELECT regexp_like('aBc', '(b|d)', 'i'); +--------------------------------------------------+ | regexp_like(Utf8("aBc"),Utf8("(b|d)"),Utf8("i")) | +--------------------------------------------------+ | true | +--------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/regexp.rs) ##### `regexp_match` Returns the first [regular expression](https://docs.rs/regex/latest/regex/#syntax) matches in a string. ```sql regexp_match(str, regexp[, flags]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to match against. Can be a constant, column, or function. - **flags**: Optional regular expression flags that control the behavior of the regular expression. The following flags are supported: - **i**: case-insensitive: letters match both upper and lower case - **m**: multi-line mode: ^ and $ match begin/end of line - **s**: allow . to match \n - **R**: enables CRLF mode: when multi-line mode is enabled, \r\n is used - **U**: swap the meaning of x* and x*? ###### Example ```sql > select regexp_match('Köln', '[a-zA-Z]ö[a-zA-Z]{2}'); +---------------------------------------------------------+ | regexp_match(Utf8("Köln"),Utf8("[a-zA-Z]ö[a-zA-Z]{2}")) | +---------------------------------------------------------+ | [Köln] | +---------------------------------------------------------+ SELECT regexp_match('aBc', '(b|d)', 'i'); +---------------------------------------------------+ | regexp_match(Utf8("aBc"),Utf8("(b|d)"),Utf8("i")) | +---------------------------------------------------+ | [B] | +---------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/regexp.rs) ##### `regexp_replace` Replaces substrings in a string that match a [regular expression](https://docs.rs/regex/latest/regex/#syntax). ```sql regexp_replace(str, regexp, replacement[, flags]) ``` ###### Arguments - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to match against. Can be a constant, column, or function. - **replacement**: Replacement string expression to operate on. Can be a constant, column, or function, and any combination of operators. - **flags**: Optional regular expression flags that control the behavior of the regular expression. The following flags are supported: - **g**: (global) Search globally and don't return after the first match - **i**: case-insensitive: letters match both upper and lower case - **m**: multi-line mode: ^ and $ match begin/end of line - **s**: allow . to match \n - **R**: enables CRLF mode: when multi-line mode is enabled, \r\n is used - **U**: swap the meaning of x* and x*? ###### Example ```sql > select regexp_replace('foobarbaz', 'b(..)', 'X\\1Y', 'g'); +------------------------------------------------------------------------+ | regexp_replace(Utf8("foobarbaz"),Utf8("b(..)"),Utf8("X\1Y"),Utf8("g")) | +------------------------------------------------------------------------+ | fooXarYXazY | +------------------------------------------------------------------------+ SELECT regexp_replace('aBc', '(b|d)', 'Ab\\1a', 'i'); +-------------------------------------------------------------------+ | regexp_replace(Utf8("aBc"),Utf8("(b|d)"),Utf8("Ab\1a"),Utf8("i")) | +-------------------------------------------------------------------+ | aAbBac | +-------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/regexp.rs) ### Time and Date Functions - [current_date](#current_date) - [current_time](#current_time) - [current_timestamp](#current_timestamp) - [date_bin](#date_bin) - [date_format](#date_format) - [date_part](#date_part) - [date_trunc](#date_trunc) - [datepart](#datepart) - [datetrunc](#datetrunc) - [from_unixtime](#from_unixtime) - [make_date](#make_date) - [make_time](#make_time) - [now](#now) - [to_char](#to_char) - [to_date](#to_date) - [to_local_time](#to_local_time) - [to_time](#to_time) - [to_timestamp](#to_timestamp) - [to_timestamp_micros](#to_timestamp_micros) - [to_timestamp_millis](#to_timestamp_millis) - [to_timestamp_nanos](#to_timestamp_nanos) - [to_timestamp_seconds](#to_timestamp_seconds) - [to_unixtime](#to_unixtime) - [today](#today) ##### `current_date` Returns the current date in the session time zone. The `current_date()` return value is determined at query time and will return the same date, no matter when in the query plan the function executes. ```sql current_date() (optional) SET datafusion.execution.time_zone = '+00:00'; SELECT current_date(); ``` ###### Example ```sql > SELECT current_date(); +----------------+ | current_date() | +----------------+ | 2024-12-23 | +----------------+ -- The current date is based on the session time zone (UTC by default) > SET datafusion.execution.time_zone = 'Asia/Tokyo'; > SELECT current_date(); +----------------+ | current_date() | +----------------+ | 2024-12-24 | +----------------+ ``` ###### Aliases - today ##### `current_time` Returns the current time in the session time zone. The `current_time()` return value is determined at query time and will return the same time, no matter when in the query plan the function executes. The session time zone can be set using the statement 'SET datafusion.execution.time_zone = desired time zone'. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql current_time() (optional) SET datafusion.execution.time_zone = '+00:00'; SELECT current_time(); ``` ###### Example ```sql > SELECT current_time(); +--------------------+ | current_time() | +--------------------+ | 06:30:00.123456789 | +--------------------+ -- The current time is based on the session time zone (UTC by default) > SET datafusion.execution.time_zone = 'Asia/Tokyo'; > SELECT current_time(); +--------------------+ | current_time() | +--------------------+ | 15:30:00.123456789 | +--------------------+ ``` ##### `current_timestamp` _Alias of [now](#now)._ ##### `date_bin` Calculates time intervals and returns the start of the interval nearest to the specified timestamp. Use `date_bin` to downsample time series data by grouping rows into time-based "bins" or "windows" and applying an aggregate or selector function to each window. For example, if you "bin" or "window" data into 15 minute intervals, an input timestamp of `2023-01-01T18:18:18Z` will be updated to the start time of the 15 minute bin it is in: `2023-01-01T18:15:00Z`. ```sql date_bin(interval, expression, origin-timestamp) ``` ###### Arguments - **interval**: Bin interval. - **expression**: Time expression to operate on. Can be a constant, column, or function. - **origin-timestamp**: Optional. Starting point used to determine bin boundaries. If not specified defaults 1970-01-01T00:00:00Z (the UNIX epoch in UTC). The following intervals are supported: - nanoseconds - microseconds - milliseconds - seconds - minutes - hours - days - weeks - months - years - century ###### Example ```sql -- Bin the timestamp into 1 day intervals > SELECT date_bin(interval '1 day', time) as bin FROM VALUES ('2023-01-01T18:18:18Z'), ('2023-01-03T19:00:03Z') t(time); +---------------------+ | bin | +---------------------+ | 2023-01-01T00:00:00 | | 2023-01-03T00:00:00 | +---------------------+ 2 row(s) fetched. -- Bin the timestamp into 1 day intervals starting at 3AM on 2023-01-01 > SELECT date_bin(interval '1 day', time, '2023-01-01T03:00:00') as bin FROM VALUES ('2023-01-01T18:18:18Z'), ('2023-01-03T19:00:03Z') t(time); +---------------------+ | bin | +---------------------+ | 2023-01-01T03:00:00 | | 2023-01-03T03:00:00 | +---------------------+ 2 row(s) fetched. -- Bin the time into 15 minute intervals starting at 1 min > SELECT date_bin(interval '15 minutes', time, TIME '00:01:00') as bin FROM VALUES (TIME '02:18:18'), (TIME '19:00:03') t(time); +----------+ | bin | +----------+ | 02:16:00 | | 18:46:00 | +----------+ 2 row(s) fetched. ``` ##### `date_format` _Alias of [to_char](#to_char)._ ##### `date_part` Returns the specified part of the date as an integer. ```sql date_part(part, expression) ``` ###### Arguments - **part**: Part of the date to return. The following date parts are supported: - year - isoyear (ISO 8601 week-numbering year) - quarter (emits value in inclusive range [1, 4] based on which quartile of the year the date is in) - month - week (week of the year) - day (day of the month) - hour - minute - second - millisecond - microsecond - nanosecond - dow (day of the week where Sunday is 0) - doy (day of the year) - epoch (seconds since Unix epoch for timestamps/dates, total seconds for intervals) - isodow (day of the week where Monday is 0) - **expression**: Time expression to operate on. Can be a constant, column, or function. ###### Example ```sql > SELECT date_part('year', '2024-05-01T00:00:00'); +-----------------------------------------------------+ | date_part(Utf8("year"),Utf8("2024-05-01T00:00:00")) | +-----------------------------------------------------+ | 2024 | +-----------------------------------------------------+ > SELECT extract(day FROM timestamp '2024-05-01T00:00:00'); +----------------------------------------------------+ | date_part(Utf8("DAY"),Utf8("2024-05-01T00:00:00")) | +----------------------------------------------------+ | 1 | +----------------------------------------------------+ ``` ###### Alternative Syntax ```sql extract(field FROM source) ``` ###### Aliases - datepart ##### `date_trunc` Truncates a timestamp or time value to a specified precision. ```sql date_trunc(precision, expression) ``` ###### Arguments - **precision**: Time precision to truncate to. The following precisions are supported: For Timestamp types: - year / YEAR - quarter / QUARTER - month / MONTH - week / WEEK - day / DAY - hour / HOUR - minute / MINUTE - second / SECOND - millisecond / MILLISECOND - microsecond / MICROSECOND For Time types (hour, minute, second, millisecond, microsecond only): - hour / HOUR - minute / MINUTE - second / SECOND - millisecond / MILLISECOND - microsecond / MICROSECOND - **expression**: Timestamp or time expression to operate on. Can be a constant, column, or function. ###### Example ```sql > SELECT date_trunc('month', '2024-05-15T10:30:00'); +-----------------------------------------------+ | date_trunc(Utf8("month"),Utf8("2024-05-15T10:30:00")) | +-----------------------------------------------+ | 2024-05-01T00:00:00 | +-----------------------------------------------+ > SELECT date_trunc('hour', '2024-05-15T10:30:00'); +----------------------------------------------+ | date_trunc(Utf8("hour"),Utf8("2024-05-15T10:30:00")) | +----------------------------------------------+ | 2024-05-15T10:00:00 | +----------------------------------------------+ ``` ###### Aliases - datetrunc ##### `datepart` _Alias of [date_part](#date_part)._ ##### `datetrunc` _Alias of [date_trunc](#date_trunc)._ ##### `from_unixtime` Converts an integer to RFC3339 timestamp format (`YYYY-MM-DDT00:00:00.000000000Z`). Integers and unsigned integers are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`) return the corresponding timestamp. ```sql from_unixtime(expression[, timezone]) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. - **timezone**: Optional timezone to use when converting the integer to a timestamp. If not provided, the default timezone is UTC. ###### Example ```sql > select from_unixtime(1599572549, 'America/New_York'); +-----------------------------------------------------------+ | from_unixtime(Int64(1599572549),Utf8("America/New_York")) | +-----------------------------------------------------------+ | 2020-09-08T09:42:29-04:00 | +-----------------------------------------------------------+ ``` ##### `make_date` Make a date from year/month/day component parts. ```sql make_date(year, month, day) ``` ###### Arguments - **year**: Year to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators. - **month**: Month to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators. - **day**: Day to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators. ###### Example ```sql > select make_date(2023, 1, 31); +-------------------------------------------+ | make_date(Int64(2023),Int64(1),Int64(31)) | +-------------------------------------------+ | 2023-01-31 | +-------------------------------------------+ > select make_date('2023', '01', '31'); +-----------------------------------------------+ | make_date(Utf8("2023"),Utf8("01"),Utf8("31")) | +-----------------------------------------------+ | 2023-01-31 | +-----------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `make_time` Make a time from hour/minute/second component parts. ```sql make_time(hour, minute, second) ``` ###### Arguments - **hour**: Hour to use when making the time. Can be a constant, column or function, and any combination of arithmetic operators. - **minute**: Minute to use when making the time. Can be a constant, column or function, and any combination of arithmetic operators. - **second**: Second to use when making the time. Can be a constant, column or function, and any combination of arithmetic operators. ###### Example ```sql > select make_time(13, 23, 1); +-------------------------------------------+ | make_time(Int64(13),Int64(23),Int64(1)) | +-------------------------------------------+ | 13:23:01 | +-------------------------------------------+ > select make_time('23', '01', '31'); +-----------------------------------------------+ | make_time(Utf8("23"),Utf8("01"),Utf8("31")) | +-----------------------------------------------+ | 23:01:31 | +-----------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `now` Returns the current timestamp in the system configured timezone (None by default). The `now()` return value is determined at query time and will return the same timestamp, no matter when in the query plan the function executes. ```sql now() ``` ###### Example ```sql > SELECT now(); +----------------------------------+ | now() | +----------------------------------+ | 2024-12-23T06:30:00.123456789 | +----------------------------------+ -- The timezone of the returned timestamp depends on the session time zone > SET datafusion.execution.time_zone = 'America/New_York'; > SELECT now(); +--------------------------------------+ | now() | +--------------------------------------+ | 2024-12-23T01:30:00.123456789-05:00 | +--------------------------------------+ ``` ###### Aliases - current_timestamp ##### `to_char` Returns a string representation of a date, time, timestamp or duration based on a [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html). Unlike the PostgreSQL equivalent of this function numerical formatting is not supported. ```sql to_char(expression, format) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function that results in a date, time, timestamp or duration. - **format**: A [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) string to use to convert the expression. - **day**: Day to use when making the date. Can be a constant, column or function, and any combination of arithmetic operators. ###### Example ```sql > select to_char('2023-03-01'::date, '%d-%m-%Y'); +----------------------------------------------+ | to_char(Utf8("2023-03-01"),Utf8("%d-%m-%Y")) | +----------------------------------------------+ | 01-03-2023 | +----------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ###### Aliases - date_format ##### `to_date` Converts a value to a date (`YYYY-MM-DD`). Supports strings, numeric and timestamp types as input. Strings are parsed as YYYY-MM-DD (e.g. '2023-07-20') if no [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)s are provided. Integers and doubles are interpreted as days since the unix epoch (`1970-01-01T00:00:00Z`). Returns the corresponding date. Note: `to_date` returns Date32, which represents its values as the number of days since unix epoch(`1970-01-01`) stored as signed 32 bit value. The largest supported date value is `9999-12-31`. ```sql to_date('2017-05-31', '%Y-%m-%d') ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. ###### Example ```sql > select to_date('2023-01-31'); +-------------------------------+ | to_date(Utf8("2023-01-31")) | +-------------------------------+ | 2023-01-31 | +-------------------------------+ > select to_date('2023/01/31', '%Y-%m-%d', '%Y/%m/%d'); +---------------------------------------------------------------------+ | to_date(Utf8("2023/01/31"),Utf8("%Y-%m-%d"),Utf8("%Y/%m/%d")) | +---------------------------------------------------------------------+ | 2023-01-31 | +---------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_local_time` Converts a timestamp with a timezone to a timestamp without a timezone (with no offset or timezone information). This function handles daylight saving time changes. ```sql to_local_time(expression) ``` ###### Arguments - **expression**: Time expression to operate on. Can be a constant, column, or function. ###### Example ```sql > SELECT to_local_time('2024-04-01T00:00:20Z'::timestamp); +---------------------------------------------+ | to_local_time(Utf8("2024-04-01T00:00:20Z")) | +---------------------------------------------+ | 2024-04-01T00:00:20 | +---------------------------------------------+ > SELECT to_local_time('2024-04-01T00:00:20Z'::timestamp AT TIME ZONE 'Europe/Brussels'); +---------------------------------------------+ | to_local_time(Utf8("2024-04-01T00:00:20Z")) | +---------------------------------------------+ | 2024-04-01T00:00:20 | +---------------------------------------------+ > SELECT time, arrow_typeof(time) as type, to_local_time(time) as to_local_time, arrow_typeof(to_local_time(time)) as to_local_time_type FROM ( SELECT '2024-04-01T00:00:20Z'::timestamp AT TIME ZONE 'Europe/Brussels' AS time ); +---------------------------+----------------------------------+---------------------+--------------------+ | time | type | to_local_time | to_local_time_type | +---------------------------+----------------------------------+---------------------+--------------------+ | 2024-04-01T00:00:20+02:00 | Timestamp(ns, "Europe/Brussels") | 2024-04-01T00:00:20 | Timestamp(ns) | +---------------------------+----------------------------------+---------------------+--------------------+ ## combine `to_local_time()` with `date_bin()` to bin on boundaries in the timezone rather ## than UTC boundaries > SELECT date_bin(interval '1 day', to_local_time('2024-04-01T00:00:20Z'::timestamp AT TIME ZONE 'Europe/Brussels')) AS date_bin; +---------------------+ | date_bin | +---------------------+ | 2024-04-01T00:00:00 | +---------------------+ > SELECT date_bin(interval '1 day', to_local_time('2024-04-01T00:00:20Z'::timestamp AT TIME ZONE 'Europe/Brussels')) AT TIME ZONE 'Europe/Brussels' AS date_bin_with_timezone; +---------------------------+ | date_bin_with_timezone | +---------------------------+ | 2024-04-01T00:00:00+02:00 | +---------------------------+ ``` ##### `to_time` Converts a value to a time (`HH:MM:SS.nnnnnnnnn`). Supports strings and timestamps as input. Strings are parsed as `HH:MM:SS`, `HH:MM:SS.nnnnnnnnn`, or `HH:MM` if no [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)s are provided. Timestamps will have the time portion extracted. Returns the corresponding time. Note: `to_time` returns Time64(Nanosecond), which represents the time of day in nanoseconds since midnight. ```sql to_time('12:30:45', '%H:%M:%S') ``` ###### Arguments - **expression**: String or Timestamp expression to operate on. Can be a constant, column, or function, and any combination of operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. ###### Example ```sql > select to_time('12:30:45'); +---------------------------+ | to_time(Utf8("12:30:45")) | +---------------------------+ | 12:30:45 | +---------------------------+ > select to_time('12-30-45', '%H-%M-%S'); +--------------------------------------------+ | to_time(Utf8("12-30-45"),Utf8("%H-%M-%S")) | +--------------------------------------------+ | 12:30:45 | +--------------------------------------------+ > select to_time('2024-01-15 14:30:45'::timestamp); +--------------------------------------------------+ | to_time(Utf8("2024-01-15 14:30:45")) | +--------------------------------------------------+ | 14:30:45 | +--------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_timestamp` Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000000`) in the session time zone. Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Strings that parse without a time zone are treated as if they are in the session time zone, or UTC if no session time zone is set. Integers, unsigned integers, and doubles are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`). Note: `to_timestamp` returns `Timestamp(ns, TimeZone)` where the time zone is the session time zone. The supported range for integer input is between`-9223372037` and `9223372036`. Supported range for string input is between `1677-09-21T00:12:44.0` and `2262-04-11T23:47:16.0`. Please use `to_timestamp_seconds` for the input outside of supported bounds. The session time zone can be set using the statement `SET TIMEZONE = 'desired time zone'`. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql to_timestamp(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. Note: parsing of named timezones (e.g. 'America/New_York') using %Z is only supported at the end of the string preceded by a space. ###### Example ```sql > select to_timestamp('2023-01-31T09:26:56.123456789-05:00'); +-----------------------------------------------------------+ | to_timestamp(Utf8("2023-01-31T09:26:56.123456789-05:00")) | +-----------------------------------------------------------+ | 2023-01-31T14:26:56.123456789 | +-----------------------------------------------------------+ > select to_timestamp('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y'); +--------------------------------------------------------------------------------------------------------+ | to_timestamp(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) | +--------------------------------------------------------------------------------------------------------+ | 2023-05-17T03:59:00.123456789 | +--------------------------------------------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_timestamp_micros` Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000000`) in the session time zone. Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Strings that parse without a time zone are treated as if they are in the session time zone, or UTC if no session time zone is set. Integers, unsigned integers, and doubles are interpreted as microseconds since the unix epoch (`1970-01-01T00:00:00Z`). The session time zone can be set using the statement `SET TIMEZONE = 'desired time zone'`. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql to_timestamp_micros(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. Note: parsing of named timezones (e.g. 'America/New_York') using %Z is only supported at the end of the string preceded by a space. ###### Example ```sql > select to_timestamp_micros('2023-01-31T09:26:56.123456789-05:00'); +------------------------------------------------------------------+ | to_timestamp_micros(Utf8("2023-01-31T09:26:56.123456789-05:00")) | +------------------------------------------------------------------+ | 2023-01-31T14:26:56.123456 | +------------------------------------------------------------------+ > select to_timestamp_micros('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y'); +---------------------------------------------------------------------------------------------------------------+ | to_timestamp_micros(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) | +---------------------------------------------------------------------------------------------------------------+ | 2023-05-17T03:59:00.123456 | +---------------------------------------------------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_timestamp_millis` Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000`) in the session time zone. Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Strings that parse without a time zone are treated as if they are in the session time zone, or UTC if no session time zone is set. Integers, unsigned integers, and doubles are interpreted as milliseconds since the unix epoch (`1970-01-01T00:00:00Z`). The session time zone can be set using the statement `SET TIMEZONE = 'desired time zone'`. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql to_timestamp_millis(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. Note: parsing of named timezones (e.g. 'America/New_York') using %Z is only supported at the end of the string preceded by a space. ###### Example ```sql > select to_timestamp_millis('2023-01-31T09:26:56.123456789-05:00'); +------------------------------------------------------------------+ | to_timestamp_millis(Utf8("2023-01-31T09:26:56.123456789-05:00")) | +------------------------------------------------------------------+ | 2023-01-31T14:26:56.123 | +------------------------------------------------------------------+ > select to_timestamp_millis('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y'); +---------------------------------------------------------------------------------------------------------------+ | to_timestamp_millis(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) | +---------------------------------------------------------------------------------------------------------------+ | 2023-05-17T03:59:00.123 | +---------------------------------------------------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_timestamp_nanos` Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000000000`) in the session time zone. Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Strings that parse without a time zone are treated as if they are in the session time zone. Integers, unsigned integers, and doubles are interpreted as nanoseconds since the unix epoch (`1970-01-01T00:00:00Z`). The session time zone can be set using the statement `SET TIMEZONE = 'desired time zone'`. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql to_timestamp_nanos(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. Note: parsing of named timezones (e.g. 'America/New_York') using %Z is only supported at the end of the string preceded by a space. ###### Example ```sql > select to_timestamp_nanos('2023-01-31T09:26:56.123456789-05:00'); +-----------------------------------------------------------------+ | to_timestamp_nanos(Utf8("2023-01-31T09:26:56.123456789-05:00")) | +-----------------------------------------------------------------+ | 2023-01-31T14:26:56.123456789 | +-----------------------------------------------------------------+ > select to_timestamp_nanos('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y'); +--------------------------------------------------------------------------------------------------------------+ | to_timestamp_nanos(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) | +--------------------------------------------------------------------------------------------------------------+ | 2023-05-17T03:59:00.123456789 | +---------------------------------------------------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_timestamp_seconds` Converts a value to a timestamp (`YYYY-MM-DDT00:00:00`) in the session time zone. Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Strings that parse without a time zone are treated as if they are in the session time zone, or UTC if no session time zone is set. Integers, unsigned integers, and doubles are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`). The session time zone can be set using the statement `SET TIMEZONE = 'desired time zone'`. The time zone can be a value like +00:00, 'Europe/London' etc. ```sql to_timestamp_seconds(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. Note: parsing of named timezones (e.g. 'America/New_York') using %Z is only supported at the end of the string preceded by a space. ###### Example ```sql > select to_timestamp_seconds('2023-01-31T09:26:56.123456789-05:00'); +-------------------------------------------------------------------+ | to_timestamp_seconds(Utf8("2023-01-31T09:26:56.123456789-05:00")) | +-------------------------------------------------------------------+ | 2023-01-31T14:26:56 | +-------------------------------------------------------------------+ > select to_timestamp_seconds('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y'); +----------------------------------------------------------------------------------------------------------------+ | to_timestamp_seconds(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) | +----------------------------------------------------------------------------------------------------------------+ | 2023-05-17T03:59:00 | +----------------------------------------------------------------------------------------------------------------+ ``` Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/builtin_functions/date_time.rs) ##### `to_unixtime` :::note GreptimeDB overrides this function. See [`to_unixtime`](/reference/sql/functions/greptimedb.md#to_unixtime) for the GreptimeDB behavior. ::: Converts a value to seconds since the unix epoch (`1970-01-01T00:00:00`). Supports strings, dates, timestamps, integer, unsigned integer, and float types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Integers, unsigned integers, and floats are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00`). ```sql to_unixtime(expression[, ..., format_n]) ``` ###### Arguments - **expression**: Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators. - **format_n**: Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned. ###### Example ```sql > select to_unixtime('2020-09-08T12:00:00+00:00'); +------------------------------------------------+ | to_unixtime(Utf8("2020-09-08T12:00:00+00:00")) | +------------------------------------------------+ | 1599566400 | +------------------------------------------------+ > select to_unixtime('01-14-2023 01:01:30+05:30', '%q', '%d-%m-%Y %H/%M/%S', '%+', '%m-%d-%Y %H:%M:%S%#z'); +-----------------------------------------------------------------------------------------------------------------------------+ | to_unixtime(Utf8("01-14-2023 01:01:30+05:30"),Utf8("%q"),Utf8("%d-%m-%Y %H/%M/%S"),Utf8("%+"),Utf8("%m-%d-%Y %H:%M:%S%#z")) | +-----------------------------------------------------------------------------------------------------------------------------+ | 1673638290 | +-----------------------------------------------------------------------------------------------------------------------------+ ``` ##### `today` _Alias of [current_date](#current_date)._ ### Array Functions - [array_any_value](#array_any_value) - [array_append](#array_append) - [array_cat](#array_cat) - [array_concat](#array_concat) - [array_contains](#array_contains) - [array_dims](#array_dims) - [array_distance](#array_distance) - [array_distinct](#array_distinct) - [array_element](#array_element) - [array_empty](#array_empty) - [array_except](#array_except) - [array_extract](#array_extract) - [array_has](#array_has) - [array_has_all](#array_has_all) - [array_has_any](#array_has_any) - [array_indexof](#array_indexof) - [array_intersect](#array_intersect) - [array_join](#array_join) - [array_length](#array_length) - [array_max](#array_max) - [array_min](#array_min) - [array_ndims](#array_ndims) - [array_pop_back](#array_pop_back) - [array_pop_front](#array_pop_front) - [array_position](#array_position) - [array_positions](#array_positions) - [array_prepend](#array_prepend) - [array_push_back](#array_push_back) - [array_push_front](#array_push_front) - [array_remove](#array_remove) - [array_remove_all](#array_remove_all) - [array_remove_n](#array_remove_n) - [array_repeat](#array_repeat) - [array_replace](#array_replace) - [array_replace_all](#array_replace_all) - [array_replace_n](#array_replace_n) - [array_resize](#array_resize) - [array_reverse](#array_reverse) - [array_slice](#array_slice) - [array_sort](#array_sort) - [array_to_string](#array_to_string) - [array_union](#array_union) - [arrays_overlap](#arrays_overlap) - [arrays_zip](#arrays_zip) - [cardinality](#cardinality) - [empty](#empty) - [flatten](#flatten) - [generate_series](#generate_series) - [list_any_value](#list_any_value) - [list_append](#list_append) - [list_cat](#list_cat) - [list_concat](#list_concat) - [list_contains](#list_contains) - [list_dims](#list_dims) - [list_distance](#list_distance) - [list_distinct](#list_distinct) - [list_element](#list_element) - [list_empty](#list_empty) - [list_except](#list_except) - [list_extract](#list_extract) - [list_has](#list_has) - [list_has_all](#list_has_all) - [list_has_any](#list_has_any) - [list_indexof](#list_indexof) - [list_intersect](#list_intersect) - [list_join](#list_join) - [list_length](#list_length) - [list_max](#list_max) - [list_ndims](#list_ndims) - [list_pop_back](#list_pop_back) - [list_pop_front](#list_pop_front) - [list_position](#list_position) - [list_positions](#list_positions) - [list_prepend](#list_prepend) - [list_push_back](#list_push_back) - [list_push_front](#list_push_front) - [list_remove](#list_remove) - [list_remove_all](#list_remove_all) - [list_remove_n](#list_remove_n) - [list_repeat](#list_repeat) - [list_replace](#list_replace) - [list_replace_all](#list_replace_all) - [list_replace_n](#list_replace_n) - [list_resize](#list_resize) - [list_reverse](#list_reverse) - [list_slice](#list_slice) - [list_sort](#list_sort) - [list_to_string](#list_to_string) - [list_union](#list_union) - [list_zip](#list_zip) - [make_array](#make_array) - [make_list](#make_list) - [range](#range) - [string_to_array](#string_to_array) - [string_to_list](#string_to_list) ##### `array_any_value` Returns the first non-null element in the array. ```sql array_any_value(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_any_value([NULL, 1, 2, 3]); +-------------------------------+ | array_any_value(List([NULL,1,2,3])) | +-------------------------------------+ | 1 | +-------------------------------------+ ``` ###### Aliases - list_any_value ##### `array_append` Appends an element to the end of an array. ```sql array_append(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to append to the array. ###### Example ```sql > select array_append([1, 2, 3], 4); +--------------------------------------+ | array_append(List([1,2,3]),Int64(4)) | +--------------------------------------+ | [1, 2, 3, 4] | +--------------------------------------+ ``` ###### Aliases - list_append - array_push_back - list_push_back ##### `array_cat` _Alias of [array_concat](#array_concat)._ ##### `array_concat` Concatenates arrays. ```sql array_concat(array[, ..., array_n]) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array_n**: Subsequent array column or literal array to concatenate. ###### Example ```sql > select array_concat([1, 2], [3, 4], [5, 6]); +---------------------------------------------------+ | array_concat(List([1,2]),List([3,4]),List([5,6])) | +---------------------------------------------------+ | [1, 2, 3, 4, 5, 6] | +---------------------------------------------------+ ``` ###### Aliases - array_cat - list_concat - list_cat ##### `array_contains` _Alias of [array_has](#array_has)._ ##### `array_dims` Returns an array of the array's dimensions. ```sql array_dims(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_dims([[1, 2, 3], [4, 5, 6]]); +---------------------------------+ | array_dims(List([1,2,3,4,5,6])) | +---------------------------------+ | [2, 3] | +---------------------------------+ ``` ###### Aliases - list_dims ##### `array_distance` Returns the Euclidean distance between two input arrays of equal length. ```sql array_distance(array1, array2) ``` ###### Arguments - **array1**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array2**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_distance([1, 2], [1, 4]); +------------------------------------+ | array_distance(List([1,2], [1,4])) | +------------------------------------+ | 2.0 | +------------------------------------+ ``` ###### Aliases - list_distance ##### `array_distinct` Returns distinct values from the array after removing duplicates. ```sql array_distinct(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_distinct([1, 3, 2, 3, 1, 2, 4]); +---------------------------------+ | array_distinct(List([1,2,3,4])) | +---------------------------------+ | [1, 2, 3, 4] | +---------------------------------+ ``` ###### Aliases - list_distinct ##### `array_element` Extracts the element with the index n from the array. ```sql array_element(array, index) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **index**: Index to extract the element from the array. ###### Example ```sql > select array_element([1, 2, 3, 4], 3); +-----------------------------------------+ | array_element(List([1,2,3,4]),Int64(3)) | +-----------------------------------------+ | 3 | +-----------------------------------------+ ``` ###### Aliases - array_extract - list_element - list_extract ##### `array_empty` _Alias of [empty](#empty)._ ##### `array_except` Returns an array of the elements that appear in the first array but not in the second. ```sql array_except(array1, array2) ``` ###### Arguments - **array1**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array2**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_except([1, 2, 3, 4], [5, 6, 3, 4]); +----------------------------------------------------+ | array_except([1, 2, 3, 4], [5, 6, 3, 4]); | +----------------------------------------------------+ | [1, 2] | +----------------------------------------------------+ > select array_except([1, 2, 3, 4], [3, 4, 5, 6]); +----------------------------------------------------+ | array_except([1, 2, 3, 4], [3, 4, 5, 6]); | +----------------------------------------------------+ | [1, 2] | +----------------------------------------------------+ ``` ###### Aliases - list_except ##### `array_extract` _Alias of [array_element](#array_element)._ ##### `array_has` Returns true if the array contains the element. ```sql array_has(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Scalar or Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_has([1, 2, 3], 2); +-----------------------------+ | array_has(List([1,2,3]), 2) | +-----------------------------+ | true | +-----------------------------+ ``` ###### Aliases - list_has - array_contains - list_contains ##### `array_has_all` Returns true if all elements of sub-array exist in array. ```sql array_has_all(array, sub-array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **sub-array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_has_all([1, 2, 3, 4], [2, 3]); +--------------------------------------------+ | array_has_all(List([1,2,3,4]), List([2,3])) | +--------------------------------------------+ | true | +--------------------------------------------+ ``` ###### Aliases - list_has_all ##### `array_has_any` Returns true if the arrays have any elements in common. ```sql array_has_any(array1, array2) ``` ###### Arguments - **array1**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array2**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_has_any([1, 2, 3], [3, 4]); +------------------------------------------+ | array_has_any(List([1,2,3]), List([3,4])) | +------------------------------------------+ | true | +------------------------------------------+ ``` ###### Aliases - list_has_any - arrays_overlap ##### `array_indexof` _Alias of [array_position](#array_position)._ ##### `array_intersect` Returns an array of elements in the intersection of array1 and array2. ```sql array_intersect(array1, array2) ``` ###### Arguments - **array1**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array2**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_intersect([1, 2, 3, 4], [5, 6, 3, 4]); +----------------------------------------------------+ | array_intersect([1, 2, 3, 4], [5, 6, 3, 4]); | +----------------------------------------------------+ | [3, 4] | +----------------------------------------------------+ > select array_intersect([1, 2, 3, 4], [5, 6, 7, 8]); +----------------------------------------------------+ | array_intersect([1, 2, 3, 4], [5, 6, 7, 8]); | +----------------------------------------------------+ | [] | +----------------------------------------------------+ ``` ###### Aliases - list_intersect ##### `array_join` _Alias of [array_to_string](#array_to_string)._ ##### `array_length` Returns the length of the array dimension. ```sql array_length(array, dimension) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **dimension**: Array dimension. ###### Example ```sql > select array_length([1, 2, 3, 4, 5], 1); +-------------------------------------------+ | array_length(List([1,2,3,4,5]), 1) | +-------------------------------------------+ | 5 | +-------------------------------------------+ ``` ###### Aliases - list_length ##### `array_max` Returns the maximum value in the array. ```sql array_max(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_max([3,1,4,2]); +-----------------------------------------+ | array_max(List([3,1,4,2])) | +-----------------------------------------+ | 4 | +-----------------------------------------+ ``` ###### Aliases - list_max ##### `array_min` Returns the minimum value in the array. ```sql array_min(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_min([3,1,4,2]); +-----------------------------------------+ | array_min(List([3,1,4,2])) | +-----------------------------------------+ | 1 | +-----------------------------------------+ ``` ##### `array_ndims` Returns the number of dimensions of the array. ```sql array_ndims(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Array element. ###### Example ```sql > select array_ndims([[1, 2, 3], [4, 5, 6]]); +----------------------------------+ | array_ndims(List([1,2,3,4,5,6])) | +----------------------------------+ | 2 | +----------------------------------+ ``` ###### Aliases - list_ndims ##### `array_pop_back` Returns the array without the last element. ```sql array_pop_back(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_pop_back([1, 2, 3]); +-------------------------------+ | array_pop_back(List([1,2,3])) | +-------------------------------+ | [1, 2] | +-------------------------------+ ``` ###### Aliases - list_pop_back ##### `array_pop_front` Returns the array without the first element. ```sql array_pop_front(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_pop_front([1, 2, 3]); +-------------------------------+ | array_pop_front(List([1,2,3])) | +-------------------------------+ | [2, 3] | +-------------------------------+ ``` ###### Aliases - list_pop_front ##### `array_position` Returns the position of the first occurrence of the specified element in the array, or NULL if not found. Comparisons are done using `IS DISTINCT FROM` semantics, so NULL is considered to match NULL. ```sql array_position(array, element) array_position(array, element, index) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to search for in the array. - **index**: Index at which to start searching (1-indexed). ###### Example ```sql > select array_position([1, 2, 2, 3, 1, 4], 2); +----------------------------------------------+ | array_position(List([1,2,2,3,1,4]),Int64(2)) | +----------------------------------------------+ | 2 | +----------------------------------------------+ > select array_position([1, 2, 2, 3, 1, 4], 2, 3); +----------------------------------------------------+ | array_position(List([1,2,2,3,1,4]),Int64(2), Int64(3)) | +----------------------------------------------------+ | 3 | +----------------------------------------------------+ ``` ###### Aliases - list_position - array_indexof - list_indexof ##### `array_positions` Searches for an element in the array, returns all occurrences. ```sql array_positions(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to search for in the array. ###### Example ```sql > select array_positions([1, 2, 2, 3, 1, 4], 2); +-----------------------------------------------+ | array_positions(List([1,2,2,3,1,4]),Int64(2)) | +-----------------------------------------------+ | [2, 3] | +-----------------------------------------------+ ``` ###### Aliases - list_positions ##### `array_prepend` Prepends an element to the beginning of an array. ```sql array_prepend(element, array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to prepend to the array. ###### Example ```sql > select array_prepend(1, [2, 3, 4]); +---------------------------------------+ | array_prepend(Int64(1),List([2,3,4])) | +---------------------------------------+ | [1, 2, 3, 4] | +---------------------------------------+ ``` ###### Aliases - list_prepend - array_push_front - list_push_front ##### `array_push_back` _Alias of [array_append](#array_append)._ ##### `array_push_front` _Alias of [array_prepend](#array_prepend)._ ##### `array_remove` Removes the first element from the array equal to the given value. NULL elements already in the array are preserved when removing a non-NULL value. If `element` evaluates to NULL, the result is NULL rather than removing NULL entries. ```sql array_remove(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to be removed from the array. ###### Example ```sql > select array_remove([1, 2, 2, 3, 2, 1, 4], 2); +----------------------------------------------+ | array_remove(List([1,2,2,3,2,1,4]),Int64(2)) | +----------------------------------------------+ | [1, 2, 3, 2, 1, 4] | +----------------------------------------------+ > select array_remove([1, 2, NULL, 2, 4], 2); +---------------------------------------------------+ | array_remove(List([1,2,NULL,2,4]),Int64(2)) | +---------------------------------------------------+ | [1, NULL, 2, 4] | +---------------------------------------------------+ ``` ###### Aliases - list_remove ##### `array_remove_all` Removes all elements from the array equal to the given value. NULL elements already in the array are preserved when removing a non-NULL value. If `element` evaluates to NULL, the result is NULL rather than removing NULL entries. ```sql array_remove_all(array, element) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to be removed from the array. ###### Example ```sql > select array_remove_all([1, 2, 2, 3, 2, 1, 4], 2); +--------------------------------------------------+ | array_remove_all(List([1,2,2,3,2,1,4]),Int64(2)) | +--------------------------------------------------+ | [1, 3, 1, 4] | +--------------------------------------------------+ > select array_remove_all([1, 2, NULL, 2, 4], 2); +-----------------------------------------------------+ | array_remove_all(List([1,2,NULL,2,4]),Int64(2)) | +-----------------------------------------------------+ | [1, NULL, 4] | +-----------------------------------------------------+ ``` ###### Aliases - list_remove_all ##### `array_remove_n` Removes the first `max` elements from the array equal to the given value. NULL elements already in the array are preserved when removing a non-NULL value. If `element` evaluates to NULL, the result is NULL rather than removing NULL entries. ```sql array_remove_n(array, element, max) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **element**: Element to be removed from the array. - **max**: Number of first occurrences to remove. ###### Example ```sql > select array_remove_n([1, 2, 2, 3, 2, 1, 4], 2, 2); +---------------------------------------------------------+ | array_remove_n(List([1,2,2,3,2,1,4]),Int64(2),Int64(2)) | +---------------------------------------------------------+ | [1, 3, 2, 1, 4] | +---------------------------------------------------------+ > select array_remove_n([1, 2, NULL, 2, 4], 2, 2); +----------------------------------------------------------+ | array_remove_n(List([1,2,NULL,2,4]),Int64(2),Int64(2)) | +----------------------------------------------------------+ | [1, NULL, 4] | +----------------------------------------------------------+ ``` ###### Aliases - list_remove_n ##### `array_repeat` Returns an array containing element `count` times. ```sql array_repeat(element, count) ``` ###### Arguments - **element**: Element expression. Can be a constant, column, or function, and any combination of array operators. - **count**: Value of how many times to repeat the element. ###### Example ```sql > select array_repeat(1, 3); +---------------------------------+ | array_repeat(Int64(1),Int64(3)) | +---------------------------------+ | [1, 1, 1] | +---------------------------------+ > select array_repeat([1, 2], 2); +------------------------------------+ | array_repeat(List([1,2]),Int64(2)) | +------------------------------------+ | [[1, 2], [1, 2]] | +------------------------------------+ ``` ###### Aliases - list_repeat ##### `array_replace` Replaces the first occurrence of the specified element with another specified element. ```sql array_replace(array, from, to) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **from**: Initial element. - **to**: Final element. ###### Example ```sql > select array_replace([1, 2, 2, 3, 2, 1, 4], 2, 5); +--------------------------------------------------------+ | array_replace(List([1,2,2,3,2,1,4]),Int64(2),Int64(5)) | +--------------------------------------------------------+ | [1, 5, 2, 3, 2, 1, 4] | +--------------------------------------------------------+ ``` ###### Aliases - list_replace ##### `array_replace_all` Replaces all occurrences of the specified element with another specified element. ```sql array_replace_all(array, from, to) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **from**: Initial element. - **to**: Final element. ###### Example ```sql > select array_replace_all([1, 2, 2, 3, 2, 1, 4], 2, 5); +------------------------------------------------------------+ | array_replace_all(List([1,2,2,3,2,1,4]),Int64(2),Int64(5)) | +------------------------------------------------------------+ | [1, 5, 5, 3, 5, 1, 4] | +------------------------------------------------------------+ ``` ###### Aliases - list_replace_all ##### `array_replace_n` Replaces the first `max` occurrences of the specified element with another specified element. ```sql array_replace_n(array, from, to, max) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **from**: Initial element. - **to**: Final element. - **max**: Number of first occurrences to replace. ###### Example ```sql > select array_replace_n([1, 2, 2, 3, 2, 1, 4], 2, 5, 2); +-------------------------------------------------------------------+ | array_replace_n(List([1,2,2,3,2,1,4]),Int64(2),Int64(5),Int64(2)) | +-------------------------------------------------------------------+ | [1, 5, 5, 3, 2, 1, 4] | +-------------------------------------------------------------------+ ``` ###### Aliases - list_replace_n ##### `array_resize` Resizes the list to contain size elements. Initializes new elements with value or empty if value is not set. ```sql array_resize(array, size, value) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **size**: New size of given array. - **value**: Defines new elements' value or empty if value is not set. ###### Example ```sql > select array_resize([1, 2, 3], 5, 0); +-------------------------------------+ | array_resize(List([1,2,3],5,0)) | +-------------------------------------+ | [1, 2, 3, 0, 0] | +-------------------------------------+ ``` ###### Aliases - list_resize ##### `array_reverse` Returns the array with the order of the elements reversed. ```sql array_reverse(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_reverse([1, 2, 3, 4]); +------------------------------------------------------------+ | array_reverse(List([1, 2, 3, 4])) | +------------------------------------------------------------+ | [4, 3, 2, 1] | +------------------------------------------------------------+ ``` ###### Aliases - list_reverse ##### `array_slice` Returns a slice of the array based on 1-indexed start and end positions. ```sql array_slice(array, begin, end) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **begin**: Index of the first element. If negative, it counts backward from the end of the array. - **end**: Index of the last element. If negative, it counts backward from the end of the array. - **stride**: Stride of the array slice. The default is 1. ###### Example ```sql > select array_slice([1, 2, 3, 4, 5, 6, 7, 8], 3, 6); +--------------------------------------------------------+ | array_slice(List([1,2,3,4,5,6,7,8]),Int64(3),Int64(6)) | +--------------------------------------------------------+ | [3, 4, 5, 6] | +--------------------------------------------------------+ ``` ###### Aliases - list_slice ##### `array_sort` Sort array. ```sql array_sort(array, desc, nulls_first) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **desc**: Whether to sort in ascending (`ASC`) or descending (`DESC`) order. The default is `ASC`. - **nulls_first**: Whether to sort nulls first (`NULLS FIRST`) or last (`NULLS LAST`). The default is `NULLS FIRST`. ###### Example ```sql > select array_sort([3, 1, 2]); +-----------------------------+ | array_sort(List([3,1,2])) | +-----------------------------+ | [1, 2, 3] | +-----------------------------+ ``` ###### Aliases - list_sort ##### `array_to_string` Converts each element to its text representation. ```sql array_to_string(array, delimiter[, null_string]) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **delimiter**: Array element separator. - **null_string**: Optional. String to use for null values in the output. If not provided, nulls will be omitted. ###### Example ```sql > select array_to_string([[1, 2, 3, 4], [5, 6, 7, 8]], ','); +----------------------------------------------------+ | array_to_string(List([1,2,3,4,5,6,7,8]),Utf8(",")) | +----------------------------------------------------+ | 1,2,3,4,5,6,7,8 | +----------------------------------------------------+ ``` ###### Aliases - list_to_string - array_join - list_join ##### `array_union` Returns an array of elements that are present in both arrays (all elements from both arrays) without duplicates. ```sql array_union(array1, array2) ``` ###### Arguments - **array1**: Array expression. Can be a constant, column, or function, and any combination of array operators. - **array2**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select array_union([1, 2, 3, 4], [5, 6, 3, 4]); +----------------------------------------------------+ | array_union([1, 2, 3, 4], [5, 6, 3, 4]); | +----------------------------------------------------+ | [1, 2, 3, 4, 5, 6] | +----------------------------------------------------+ > select array_union([1, 2, 3, 4], [5, 6, 7, 8]); +----------------------------------------------------+ | array_union([1, 2, 3, 4], [5, 6, 7, 8]); | +----------------------------------------------------+ | [1, 2, 3, 4, 5, 6, 7, 8] | +----------------------------------------------------+ ``` ###### Aliases - list_union ##### `arrays_overlap` _Alias of [array_has_any](#array_has_any)._ ##### `arrays_zip` Returns an array of structs created by combining the elements of each input array at the same index. If the arrays have different lengths, shorter arrays are padded with NULLs. ```sql arrays_zip(array1[, ..., array_n]) ``` ###### Arguments - **array1**: First array expression. - **array_n**: Optional additional array expressions. ###### Example ```sql > select arrays_zip([1, 2, 3]); +---------------------------------------------------+ | arrays_zip([1, 2, 3]) | +---------------------------------------------------+ | [{1: 1}, {1: 2}, {1: 3}] | +---------------------------------------------------+ > select arrays_zip([1, 2], [3, 4, 5]); +---------------------------------------------------+ | arrays_zip([1, 2], [3, 4, 5]) | +---------------------------------------------------+ | [{1: 1, 2: 3}, {1: 2, 2: 4}, {1: NULL, 2: 5}] | +---------------------------------------------------+ ``` ###### Aliases - list_zip ##### `cardinality` Returns the total number of elements in the array. ```sql cardinality(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select cardinality([[1, 2, 3, 4], [5, 6, 7, 8]]); +--------------------------------------+ | cardinality(List([1,2,3,4,5,6,7,8])) | +--------------------------------------+ | 8 | +--------------------------------------+ ``` ##### `empty` Returns 1 for an empty array or 0 for a non-empty array. ```sql empty(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select empty([1]); +------------------+ | empty(List([1])) | +------------------+ | 0 | +------------------+ ``` ###### Aliases - array_empty - list_empty ##### `flatten` Converts an array of arrays to a flat array. - Applies to any depth of nested arrays - Does not change arrays that are already flat The flattened array contains all the elements from all source arrays. ```sql flatten(array) ``` ###### Arguments - **array**: Array expression. Can be a constant, column, or function, and any combination of array operators. ###### Example ```sql > select flatten([[1, 2], [3, 4]]); +------------------------------+ | flatten(List([1,2], [3,4])) | +------------------------------+ | [1, 2, 3, 4] | +------------------------------+ ``` ##### `generate_series` Similar to the range function, but it includes the upper bound. ```sql generate_series(stop) generate_series(start, stop[, step]) ``` ###### Arguments - **start**: Start of the series. Ints, timestamps, dates or string types that can be coerced to Date32 are supported. - **end**: End of the series (included). Type must be the same as start. - **step**: Increase by step (can not be 0). Steps less than a day are supported only for timestamp ranges. ###### Example ```sql > select generate_series(1,3); +------------------------------------+ | generate_series(Int64(1),Int64(3)) | +------------------------------------+ | [1, 2, 3] | +------------------------------------+ ``` ##### `list_any_value` _Alias of [array_any_value](#array_any_value)._ ##### `list_append` _Alias of [array_append](#array_append)._ ##### `list_cat` _Alias of [array_concat](#array_concat)._ ##### `list_concat` _Alias of [array_concat](#array_concat)._ ##### `list_contains` _Alias of [array_has](#array_has)._ ##### `list_dims` _Alias of [array_dims](#array_dims)._ ##### `list_distance` _Alias of [array_distance](#array_distance)._ ##### `list_distinct` _Alias of [array_distinct](#array_distinct)._ ##### `list_element` _Alias of [array_element](#array_element)._ ##### `list_empty` _Alias of [empty](#empty)._ ##### `list_except` _Alias of [array_except](#array_except)._ ##### `list_extract` _Alias of [array_element](#array_element)._ ##### `list_has` _Alias of [array_has](#array_has)._ ##### `list_has_all` _Alias of [array_has_all](#array_has_all)._ ##### `list_has_any` _Alias of [array_has_any](#array_has_any)._ ##### `list_indexof` _Alias of [array_position](#array_position)._ ##### `list_intersect` _Alias of [array_intersect](#array_intersect)._ ##### `list_join` _Alias of [array_to_string](#array_to_string)._ ##### `list_length` _Alias of [array_length](#array_length)._ ##### `list_max` _Alias of [array_max](#array_max)._ ##### `list_ndims` _Alias of [array_ndims](#array_ndims)._ ##### `list_pop_back` _Alias of [array_pop_back](#array_pop_back)._ ##### `list_pop_front` _Alias of [array_pop_front](#array_pop_front)._ ##### `list_position` _Alias of [array_position](#array_position)._ ##### `list_positions` _Alias of [array_positions](#array_positions)._ ##### `list_prepend` _Alias of [array_prepend](#array_prepend)._ ##### `list_push_back` _Alias of [array_append](#array_append)._ ##### `list_push_front` _Alias of [array_prepend](#array_prepend)._ ##### `list_remove` _Alias of [array_remove](#array_remove)._ ##### `list_remove_all` _Alias of [array_remove_all](#array_remove_all)._ ##### `list_remove_n` _Alias of [array_remove_n](#array_remove_n)._ ##### `list_repeat` _Alias of [array_repeat](#array_repeat)._ ##### `list_replace` _Alias of [array_replace](#array_replace)._ ##### `list_replace_all` _Alias of [array_replace_all](#array_replace_all)._ ##### `list_replace_n` _Alias of [array_replace_n](#array_replace_n)._ ##### `list_resize` _Alias of [array_resize](#array_resize)._ ##### `list_reverse` _Alias of [array_reverse](#array_reverse)._ ##### `list_slice` _Alias of [array_slice](#array_slice)._ ##### `list_sort` _Alias of [array_sort](#array_sort)._ ##### `list_to_string` _Alias of [array_to_string](#array_to_string)._ ##### `list_union` _Alias of [array_union](#array_union)._ ##### `list_zip` _Alias of [arrays_zip](#arrays_zip)._ ##### `make_array` Returns an array using the specified input expressions. ```sql make_array(expression1[, ..., expression_n]) ``` ###### Arguments - **expression_n**: Expression to include in the output array. Can be a constant, column, or function, and any combination of arithmetic or string operators. ###### Example ```sql > select make_array(1, 2, 3, 4, 5); +----------------------------------------------------------+ | make_array(Int64(1),Int64(2),Int64(3),Int64(4),Int64(5)) | +----------------------------------------------------------+ | [1, 2, 3, 4, 5] | +----------------------------------------------------------+ ``` ###### Aliases - make_list ##### `make_list` _Alias of [make_array](#make_array)._ ##### `range` Returns an Arrow array between start and stop with step. The range start..end contains all values with start <= x < end. It is empty if start >= end. Step cannot be 0. ```sql range(stop) range(start, stop[, step]) ``` ###### Arguments - **start**: Start of the range. Ints, timestamps, dates or string types that can be coerced to Date32 are supported. - **end**: End of the range (not included). Type must be the same as start. - **step**: Increase by step (cannot be 0). Steps less than a day are supported only for timestamp ranges. ###### Example ```sql > select range(2, 10, 3); +-----------------------------------+ | range(Int64(2),Int64(10),Int64(3))| +-----------------------------------+ | [2, 5, 8] | +-----------------------------------+ > select range(DATE '1992-09-01', DATE '1993-03-01', INTERVAL '1' MONTH); +--------------------------------------------------------------------------+ | range(DATE '1992-09-01', DATE '1993-03-01', INTERVAL '1' MONTH) | +--------------------------------------------------------------------------+ | [1992-09-01, 1992-10-01, 1992-11-01, 1992-12-01, 1993-01-01, 1993-02-01] | +--------------------------------------------------------------------------+ ``` ##### `string_to_array` Splits a string into an array of substrings based on a delimiter. Any substrings matching the optional `null_str` argument are replaced with NULL. ```sql string_to_array(str, delimiter[, null_str]) ``` ###### Arguments - **str**: String expression to split. - **delimiter**: Delimiter string to split on. - **null_str**: Substring values to be replaced with `NULL`. ###### Example ```sql > select string_to_array('abc##def', '##'); +-----------------------------------+ | string_to_array(Utf8('abc##def')) | +-----------------------------------+ | ['abc', 'def'] | +-----------------------------------+ > select string_to_array('abc def', ' ', 'def'); +---------------------------------------------+ | string_to_array(Utf8('abc def'), Utf8(' '), Utf8('def')) | +---------------------------------------------+ | ['abc', NULL] | +---------------------------------------------+ ``` ###### Aliases - string_to_list ##### `string_to_list` _Alias of [string_to_array](#string_to_array)._ ### Struct Functions - [named_struct](#named_struct) - [row](#row) - [struct](#struct) ##### `named_struct` Returns an Arrow struct using the specified name and input expressions pairs. For information on comparing and ordering struct values (including `NULL` handling), see [Comparison and Ordering](df-functions/struct_coercion.md#comparison-and-ordering). ```sql named_struct(expression1_name, expression1_input[, ..., expression_n_name, expression_n_input]) ``` ###### Arguments - **expression_n_name**: Name of the column field. Must be a constant string. - **expression_n_input**: Expression to include in the output struct. Can be a constant, column, or function, and any combination of arithmetic or string operators. ###### Example For example, this query converts two columns `a` and `b` to a single column with a struct type of fields `field_a` and `field_b`: ```sql > select * from t; +---+---+ | a | b | +---+---+ | 1 | 2 | | 3 | 4 | +---+---+ > select named_struct('field_a', a, 'field_b', b) from t; +-------------------------------------------------------+ | named_struct(Utf8("field_a"),t.a,Utf8("field_b"),t.b) | +-------------------------------------------------------+ | {field_a: 1, field_b: 2} | | {field_a: 3, field_b: 4} | +-------------------------------------------------------+ ``` ##### `row` _Alias of [struct](#struct)._ ##### `struct` Returns an Arrow struct using the specified input expressions optionally named. Fields in the returned struct use the optional name or the `cN` naming convention. For example: `c0`, `c1`, `c2`, etc. For information on comparing and ordering struct values (including `NULL` handling), see [Comparison and Ordering](df-functions/struct_coercion.md#comparison-and-ordering). ```sql struct(expression1[, ..., expression_n]) ``` ###### Arguments - **expression1, expression_n**: Expression to include in the output struct. Can be a constant, column, or function, any combination of arithmetic or string operators. ###### Example For example, this query converts two columns `a` and `b` to a single column with a struct type of fields `field_a` and `c1`: ```sql > select * from t; +---+---+ | a | b | +---+---+ | 1 | 2 | | 3 | 4 | +---+---+ -- use default names `c0`, `c1` > select struct(a, b) from t; +-----------------+ | struct(t.a,t.b) | +-----------------+ | {c0: 1, c1: 2} | | {c0: 3, c1: 4} | +-----------------+ -- name the first field `field_a` select struct(a as field_a, b) from t; +--------------------------------------------------+ | named_struct(Utf8("field_a"),t.a,Utf8("c1"),t.b) | +--------------------------------------------------+ | {field_a: 1, c1: 2} | | {field_a: 3, c1: 4} | +--------------------------------------------------+ ``` ###### Aliases - row ### Map Functions - [element_at](#element_at) - [map](#map) - [map_entries](#map_entries) - [map_extract](#map_extract) - [map_keys](#map_keys) - [map_values](#map_values) ##### `element_at` _Alias of [map_extract](#map_extract)._ ##### `map` Returns an Arrow map with the specified key-value pairs. The `make_map` function creates a map from two lists: one for keys and one for values. Each key must be unique and non-null. ```sql map(key, value) map(key: value) make_map(['key1', 'key2'], ['value1', 'value2']) ``` ###### Arguments - **key**: For `map`: Expression to be used for key. Can be a constant, column, function, or any combination of arithmetic or string operators. For `make_map`: The list of keys to be used in the map. Each key must be unique and non-null. - **value**: For `map`: Expression to be used for value. Can be a constant, column, function, or any combination of arithmetic or string operators. For `make_map`: The list of values to be mapped to the corresponding keys. ###### Example ```sql -- Using map function SELECT MAP('type', 'test'); ---- {type: test} SELECT MAP(['POST', 'HEAD', 'PATCH'], [41, 33, null]); ---- {POST: 41, HEAD: 33, PATCH: NULL} SELECT MAP([[1,2], [3,4]], ['a', 'b']); ---- {[1, 2]: a, [3, 4]: b} SELECT MAP { 'a': 1, 'b': 2 }; ---- {a: 1, b: 2} -- Using make_map function SELECT MAKE_MAP(['POST', 'HEAD'], [41, 33]); ---- {POST: 41, HEAD: 33} SELECT MAKE_MAP(['key1', 'key2'], ['value1', null]); ---- {key1: value1, key2: } ``` ##### `map_entries` Returns a list of all entries in the map. ```sql map_entries(map) ``` ###### Arguments - **map**: Map expression. Can be a constant, column, or function, and any combination of map operators. ###### Example ```sql SELECT map_entries(MAP {'a': 1, 'b': NULL, 'c': 3}); ---- [{'key': a, 'value': 1}, {'key': b, 'value': NULL}, {'key': c, 'value': 3}] SELECT map_entries(map([100, 5], [42, 43])); ---- [{'key': 100, 'value': 42}, {'key': 5, 'value': 43}] ``` ##### `map_extract` Returns a list containing the value for the given key or an empty list if the key is not present in the map. ```sql map_extract(map, key) ``` ###### Arguments - **map**: Map expression. Can be a constant, column, or function, and any combination of map operators. - **key**: Key to extract from the map. Can be a constant, column, or function, any combination of arithmetic or string operators, or a named expression of the previously listed. ###### Example ```sql SELECT map_extract(MAP {'a': 1, 'b': NULL, 'c': 3}, 'a'); ---- [1] SELECT map_extract(MAP {1: 'one', 2: 'two'}, 2); ---- ['two'] SELECT map_extract(MAP {'x': 10, 'y': NULL, 'z': 30}, 'y'); ---- [] ``` ###### Aliases - element_at ##### `map_keys` Returns a list of all keys in the map. ```sql map_keys(map) ``` ###### Arguments - **map**: Map expression. Can be a constant, column, or function, and any combination of map operators. ###### Example ```sql SELECT map_keys(MAP {'a': 1, 'b': NULL, 'c': 3}); ---- [a, b, c] SELECT map_keys(map([100, 5], [42, 43])); ---- [100, 5] ``` ##### `map_values` Returns a list of all values in the map. ```sql map_values(map) ``` ###### Arguments - **map**: Map expression. Can be a constant, column, or function, and any combination of map operators. ###### Example ```sql SELECT map_values(MAP {'a': 1, 'b': NULL, 'c': 3}); ---- [1, , 3] SELECT map_values(map([100, 5], [42, 43])); ---- [42, 43] ``` ### Hashing Functions - [digest](#digest) - [md5](#md5) - [sha224](#sha224) - [sha256](#sha256) - [sha384](#sha384) - [sha512](#sha512) ##### `digest` Computes the binary hash of an expression using the specified algorithm. ```sql digest(expression, algorithm) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **algorithm**: String expression specifying algorithm to use. Must be one of: - md5 - sha224 - sha256 - sha384 - sha512 - blake2s - blake2b - blake3 ###### Example ```sql > select digest('foo', 'sha256'); +------------------------------------------------------------------+ | digest(Utf8("foo"),Utf8("sha256")) | +------------------------------------------------------------------+ | 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae | +------------------------------------------------------------------+ ``` ##### `md5` Computes an MD5 128-bit checksum for a string expression. ```sql md5(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select md5('foo'); +----------------------------------+ | md5(Utf8("foo")) | +----------------------------------+ | acbd18db4cc2f85cedef654fccc4a4d8 | +----------------------------------+ ``` ##### `sha224` Computes the SHA-224 hash of a binary string. ```sql sha224(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select sha224('foo'); +----------------------------------------------------------+ | sha224(Utf8("foo")) | +----------------------------------------------------------+ | 0808f64e60d58979fcb676c96ec938270dea42445aeefcd3a4e6f8db | +----------------------------------------------------------+ ``` ##### `sha256` Computes the SHA-256 hash of a binary string. ```sql sha256(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select sha256('foo'); +------------------------------------------------------------------+ | sha256(Utf8("foo")) | +------------------------------------------------------------------+ | 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae | +------------------------------------------------------------------+ ``` ##### `sha384` Computes the SHA-384 hash of a binary string. ```sql sha384(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select sha384('foo'); +--------------------------------------------------------------------------------------------------+ | sha384(Utf8("foo")) | +--------------------------------------------------------------------------------------------------+ | 98c11ffdfdd540676b1a137cb1a22b2a70350c9a44171d6b1180c6be5cbb2ee3f79d532c8a1dd9ef2e8e08e752a3babb | +--------------------------------------------------------------------------------------------------+ ``` ##### `sha512` Computes the SHA-512 hash of a binary string. ```sql sha512(expression) ``` ###### Arguments - **expression**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select sha512('foo'); +----------------------------------------------------------------------------------------------------------------------------------+ | sha512(Utf8("foo")) | +----------------------------------------------------------------------------------------------------------------------------------+ | f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7 | +----------------------------------------------------------------------------------------------------------------------------------+ ``` ### Union Functions Functions to work with the union data type, also know as tagged unions, variant types, enums or sum types. Note: Not related to the SQL UNION operator - [union_extract](#union_extract) - [union_tag](#union_tag) ##### `union_extract` Returns the value of the given field in the union when selected, or NULL otherwise. ```sql union_extract(union, field_name) ``` ###### Arguments - **union**: Union expression to operate on. Can be a constant, column, or function, and any combination of operators. - **field_name**: String expression to operate on. Must be a constant. ###### Example ```sql ❯ select union_column, union_extract(union_column, 'a'), union_extract(union_column, 'b') from table_with_union; +--------------+----------------------------------+----------------------------------+ | union_column | union_extract(union_column, 'a') | union_extract(union_column, 'b') | +--------------+----------------------------------+----------------------------------+ | {a=1} | 1 | | | {b=3.0} | | 3.0 | | {a=4} | 4 | | | {b=} | | | | {a=} | | | +--------------+----------------------------------+----------------------------------+ ``` ##### `union_tag` Returns the name of the currently selected field in the union ```sql union_tag(union_expression) ``` ###### Arguments - **union**: Union expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql ❯ select union_column, union_tag(union_column) from table_with_union; +--------------+-------------------------+ | union_column | union_tag(union_column) | +--------------+-------------------------+ | {a=1} | a | | {b=3.0} | b | | {a=4} | a | | {b=} | b | | {a=} | a | +--------------+-------------------------+ ``` ### Other Functions - [arrow_cast](#arrow_cast) - [arrow_metadata](#arrow_metadata) - [arrow_try_cast](#arrow_try_cast) - [arrow_typeof](#arrow_typeof) - [get_field](#get_field) - [version](#version) ##### `arrow_cast` Casts a value to a specific Arrow data type. ```sql arrow_cast(expression, datatype) ``` ###### Arguments - **expression**: Expression to cast. The expression can be a constant, column, or function, and any combination of operators. - **datatype**: [Arrow data type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) name to cast to, as a string. The format is the same as that returned by [`arrow_typeof`] ###### Example ```sql > select arrow_cast(-5, 'Int8') as a, arrow_cast('foo', 'Dictionary(Int32, Utf8)') as b, arrow_cast('bar', 'LargeUtf8') as c; +----+-----+-----+ | a | b | c | +----+-----+-----+ | -5 | foo | bar | +----+-----+-----+ > select arrow_cast('2023-01-02T12:53:02', 'Timestamp(µs, "+08:00")') as d, arrow_cast('2023-01-02T12:53:02', 'Timestamp(µs)') as e; +---------------------------+---------------------+ | d | e | +---------------------------+---------------------+ | 2023-01-02T12:53:02+08:00 | 2023-01-02T12:53:02 | +---------------------------+---------------------+ ``` ##### `arrow_metadata` Returns the metadata of the input expression. If a key is provided, returns the value for that key. If no key is provided, returns a Map of all metadata. ```sql arrow_metadata(expression[, key]) ``` ###### Arguments - **expression**: The expression to retrieve metadata from. Can be a column or other expression. - **key**: Optional. The specific metadata key to retrieve. ###### Example ```sql > select arrow_metadata(col) from table; +----------------------------+ | arrow_metadata(table.col) | +----------------------------+ | {k: v} | +----------------------------+ > select arrow_metadata(col, 'k') from table; +-------------------------------+ | arrow_metadata(table.col, 'k')| +-------------------------------+ | v | +-------------------------------+ ``` ##### `arrow_try_cast` Casts a value to a specific Arrow data type, returning NULL if the cast fails. ```sql arrow_try_cast(expression, datatype) ``` ###### Arguments - **expression**: Expression to cast. The expression can be a constant, column, or function, and any combination of operators. - **datatype**: [Arrow data type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) name to cast to, as a string. The format is the same as that returned by [`arrow_typeof`] ###### Example ```sql > select arrow_try_cast('123', 'Int64') as a, arrow_try_cast('not_a_number', 'Int64') as b; +-----+------+ | a | b | +-----+------+ | 123 | NULL | +-----+------+ ``` ##### `arrow_typeof` Returns the name of the underlying [Arrow data type](https://docs.rs/arrow/latest/arrow/datatypes/enum.DataType.html) of the expression. ```sql arrow_typeof(expression) ``` ###### Arguments - **expression**: Expression to evaluate. The expression can be a constant, column, or function, and any combination of operators. ###### Example ```sql > select arrow_typeof('foo'), arrow_typeof(1); +---------------------------+------------------------+ | arrow_typeof(Utf8("foo")) | arrow_typeof(Int64(1)) | +---------------------------+------------------------+ | Utf8 | Int64 | +---------------------------+------------------------+ ``` ##### `get_field` Returns a field within a map or a struct with the given key. Supports nested field access by providing multiple field names. Note: most users invoke `get_field` indirectly via field access syntax such as `my_struct_col['field_name']` which results in a call to `get_field(my_struct_col, 'field_name')`. Nested access like `my_struct['a']['b']` is optimized to a single call: `get_field(my_struct, 'a', 'b')`. ```sql get_field(expression, field_name[, field_name2, ...]) ``` ###### Arguments - **expression**: The map or struct to retrieve a field from. - **field_name**: The field name(s) to access, in order for nested access. Must evaluate to strings. ###### Example ```sql > -- Access a field from a struct column > create table test( struct_col) as values ({name: 'Alice', age: 30}), ({name: 'Bob', age: 25}); > select struct_col from test; +-----------------------------+ | struct_col | +-----------------------------+ | {name: Alice, age: 30} | | {name: Bob, age: 25} | +-----------------------------+ > select struct_col['name'] as name from test; +-------+ | name | +-------+ | Alice | | Bob | +-------+ > -- Nested field access with multiple arguments > create table test(struct_col) as values ({outer: {inner_val: 42}}); > select struct_col['outer']['inner_val'] as result from test; +--------+ | result | +--------+ | 42 | +--------+ ``` ##### `version` Returns the version of DataFusion. ```sql version() ``` ###### Example ```sql > select version(); +--------------------------------------------+ | version() | +--------------------------------------------+ | Apache DataFusion 42.0.0, aarch64 on macos | +--------------------------------------------+ ``` ## Aggregate Functions Aggregate functions operate on a set of values to compute a single result. ### Filter clause Aggregate functions support the SQL `FILTER (WHERE ...)` clause to restrict which input rows contribute to the aggregate result. ```sql function([exprs]) FILTER (WHERE condition) ``` Example: ```sql SELECT sum(salary) FILTER (WHERE salary > 0) AS sum_positive_salaries, count(*) FILTER (WHERE active) AS active_count FROM employees; ``` Note: When no rows pass the filter, `COUNT` returns `0` while `SUM`/`AVG`/`MIN`/`MAX` return `NULL`. ### WITHIN GROUP / Ordered-set aggregates Some aggregate functions accept the SQL `WITHIN GROUP (ORDER BY ...)` clause to specify the ordering the aggregate relies on. In DataFusion this is opt-in: only aggregate functions whose implementation returns `true` from `AggregateUDFImpl::supports_within_group_clause()` accept the `WITHIN GROUP` clause. Attempting to use `WITHIN GROUP` with a regular aggregate (for example, `SELECT SUM(x) WITHIN GROUP (ORDER BY x)`) will fail during planning with an error: "WITHIN GROUP is only supported for ordered-set aggregate functions". Currently, the built-in aggregate functions that support `WITHIN GROUP` are: - `percentile_cont` — exact percentile aggregate (also available as `percentile_cont(column, percentile)`) - `approx_percentile_cont` — approximate percentile using the t-digest algorithm - `approx_percentile_cont_with_weight` — approximate weighted percentile using the t-digest algorithm Note: rank-like functions such as `rank()`, `dense_rank()`, and `percent_rank()` are window functions and use the `OVER (...)` clause; they are not ordered-set aggregates that accept `WITHIN GROUP` in DataFusion. Example (ordered-set aggregate): ```sql percentile_cont(0.5) WITHIN GROUP (ORDER BY value) ``` Example (invalid usage — planner will error): ```sql -- This will fail: SUM is not an ordered-set aggregate SELECT SUM(x) WITHIN GROUP (ORDER BY x) FROM t; ``` ### General Functions - [array_agg](#array_agg) - [avg](#avg) - [bit_and](#bit_and) - [bit_or](#bit_or) - [bit_xor](#bit_xor) - [bool_and](#bool_and) - [bool_or](#bool_or) - [count](#count) - [first_value](#first_value) - [grouping](#grouping) - [last_value](#last_value) - [max](#max) - [mean](#mean) - [median](#median) - [min](#min) - [percentile_cont](#percentile_cont) - [quantile_cont](#quantile_cont) - [string_agg](#string_agg) - [sum](#sum) - [var](#var) - [var_pop](#var_pop) - [var_population](#var_population) - [var_samp](#var_samp) - [var_sample](#var_sample) ##### `array_agg` Returns an array created from the expression elements. If ordering is required, elements are inserted in the specified order. This aggregation function can only mix DISTINCT and ORDER BY if the ordering expression is exactly the same as the argument expression. ```sql array_agg(expression [ORDER BY expression]) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT array_agg(column_name ORDER BY other_column) FROM table_name; +-----------------------------------------------+ | array_agg(column_name ORDER BY other_column) | +-----------------------------------------------+ | [element1, element2, element3] | +-----------------------------------------------+ > SELECT array_agg(DISTINCT column_name ORDER BY column_name) FROM table_name; +--------------------------------------------------------+ | array_agg(DISTINCT column_name ORDER BY column_name) | +--------------------------------------------------------+ | [element1, element2, element3] | +--------------------------------------------------------+ ``` ##### `avg` Returns the average of numeric values in the specified column. ```sql avg(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT avg(column_name) FROM table_name; +---------------------------+ | avg(column_name) | +---------------------------+ | 42.75 | +---------------------------+ ``` ###### Aliases - mean ##### `bit_and` Computes the bitwise AND of all non-null input values. ```sql bit_and(expression) ``` ###### Arguments - **expression**: Integer expression to operate on. Can be a constant, column, or function, and any combination of operators. ##### `bit_or` Computes the bitwise OR of all non-null input values. ```sql bit_or(expression) ``` ###### Arguments - **expression**: Integer expression to operate on. Can be a constant, column, or function, and any combination of operators. ##### `bit_xor` Computes the bitwise exclusive OR of all non-null input values. ```sql bit_xor(expression) ``` ###### Arguments - **expression**: Integer expression to operate on. Can be a constant, column, or function, and any combination of operators. ##### `bool_and` Returns true if all non-null input values are true, otherwise false. ```sql bool_and(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT bool_and(column_name) FROM table_name; +----------------------------+ | bool_and(column_name) | +----------------------------+ | true | +----------------------------+ ``` ##### `bool_or` Returns true if all non-null input values are true, otherwise false. ```sql bool_and(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT bool_and(column_name) FROM table_name; +----------------------------+ | bool_and(column_name) | +----------------------------+ | true | +----------------------------+ ``` ##### `count` Returns the number of non-null values in the specified column. To include null values in the total count, use `count(*)`. ```sql count(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT count(column_name) FROM table_name; +-----------------------+ | count(column_name) | +-----------------------+ | 100 | +-----------------------+ > SELECT count(*) FROM table_name; +------------------+ | count(*) | +------------------+ | 120 | +------------------+ ``` ##### `first_value` Returns the first element in an aggregation group according to the requested ordering. If no ordering is given, returns an arbitrary element from the group. ```sql first_value(expression [ORDER BY expression]) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT first_value(column_name ORDER BY other_column) FROM table_name; +-----------------------------------------------+ | first_value(column_name ORDER BY other_column)| +-----------------------------------------------+ | first_element | +-----------------------------------------------+ ``` ##### `grouping` Returns 1 if the data is aggregated across the specified column, or 0 if it is not aggregated in the result set. ```sql grouping(expression) ``` ###### Arguments - **expression**: Expression to evaluate whether data is aggregated across the specified column. Can be a constant, column, or function. ###### Example ```sql > SELECT column_name, GROUPING(column_name) AS group_column FROM table_name GROUP BY GROUPING SETS ((column_name), ()); +-------------+-------------+ | column_name | group_column | +-------------+-------------+ | value1 | 0 | | value2 | 0 | | NULL | 1 | +-------------+-------------+ ``` ##### `last_value` Returns the last element in an aggregation group according to the requested ordering. If no ordering is given, returns an arbitrary element from the group. ```sql last_value(expression [ORDER BY expression]) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT last_value(column_name ORDER BY other_column) FROM table_name; +-----------------------------------------------+ | last_value(column_name ORDER BY other_column) | +-----------------------------------------------+ | last_element | +-----------------------------------------------+ ``` ##### `max` Returns the maximum value in the specified column. ```sql max(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT max(column_name) FROM table_name; +----------------------+ | max(column_name) | +----------------------+ | 150 | +----------------------+ ``` ##### `mean` _Alias of [avg](#avg)._ ##### `median` Returns the median value in the specified column. ```sql median(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT median(column_name) FROM table_name; +----------------------+ | median(column_name) | +----------------------+ | 45.5 | +----------------------+ ``` ##### `min` Returns the minimum value in the specified column. ```sql min(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT min(column_name) FROM table_name; +----------------------+ | min(column_name) | +----------------------+ | 12 | +----------------------+ ``` ##### `percentile_cont` Returns the exact percentile of input values, interpolating between values if needed. ```sql percentile_cont(percentile) WITHIN GROUP (ORDER BY expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. - **percentile**: Percentile to compute. Must be a float value between 0 and 1 (inclusive). ###### Example ```sql > SELECT percentile_cont(0.75) WITHIN GROUP (ORDER BY column_name) FROM table_name; +----------------------------------------------------------+ | percentile_cont(0.75) WITHIN GROUP (ORDER BY column_name) | +----------------------------------------------------------+ | 45.5 | +----------------------------------------------------------+ ``` An alternate syntax is also supported: ```sql > SELECT percentile_cont(column_name, 0.75) FROM table_name; +---------------------------------------+ | percentile_cont(column_name, 0.75) | +---------------------------------------+ | 45.5 | +---------------------------------------+ ``` ###### Aliases - quantile_cont ##### `quantile_cont` _Alias of [percentile_cont](#percentile_cont)._ ##### `string_agg` Concatenates the values of string expressions and places separator values between them. If ordering is required, strings are concatenated in the specified order. This aggregation function can only mix DISTINCT and ORDER BY if the ordering expression is exactly the same as the first argument expression. ```sql string_agg([DISTINCT] expression, delimiter [ORDER BY expression]) ``` ###### Arguments - **expression**: The string expression to concatenate. Can be a column or any valid string expression. - **delimiter**: A literal string used as a separator between the concatenated values. ###### Example ```sql > SELECT string_agg(name, ', ') AS names_list FROM employee; +--------------------------+ | names_list | +--------------------------+ | Alice, Bob, Bob, Charlie | +--------------------------+ > SELECT string_agg(name, ', ' ORDER BY name DESC) AS names_list FROM employee; +--------------------------+ | names_list | +--------------------------+ | Charlie, Bob, Bob, Alice | +--------------------------+ > SELECT string_agg(DISTINCT name, ', ' ORDER BY name DESC) AS names_list FROM employee; +--------------------------+ | names_list | +--------------------------+ | Charlie, Bob, Alice | +--------------------------+ ``` ##### `sum` Returns the sum of all values in the specified column. ```sql sum(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT sum(column_name) FROM table_name; +-----------------------+ | sum(column_name) | +-----------------------+ | 12345 | +-----------------------+ ``` ##### `var` Returns the statistical sample variance of a set of numbers. ```sql var(expression) ``` ###### Arguments - **expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Aliases - var_sample - var_samp ##### `var_pop` Returns the statistical population variance of a set of numbers. ```sql var_pop(expression) ``` ###### Arguments - **expression**: Numeric expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Aliases - var_population ##### `var_population` _Alias of [var_pop](#var_pop)._ ##### `var_samp` _Alias of [var](#var)._ ##### `var_sample` _Alias of [var](#var)._ ### Statistical Functions - [corr](#corr) - [covar](#covar) - [covar_pop](#covar_pop) - [covar_samp](#covar_samp) - [nth_value](#nth_value) - [regr_avgx](#regr_avgx) - [regr_avgy](#regr_avgy) - [regr_count](#regr_count) - [regr_intercept](#regr_intercept) - [regr_r2](#regr_r2) - [regr_slope](#regr_slope) - [regr_sxx](#regr_sxx) - [regr_sxy](#regr_sxy) - [regr_syy](#regr_syy) - [stddev](#stddev) - [stddev_pop](#stddev_pop) - [stddev_samp](#stddev_samp) ##### `corr` Returns the coefficient of correlation between two numeric values. ```sql corr(expression1, expression2) ``` ###### Arguments - **expression1**: First expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression2**: Second expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT corr(column1, column2) FROM table_name; +--------------------------------+ | corr(column1, column2) | +--------------------------------+ | 0.85 | +--------------------------------+ ``` ##### `covar` _Alias of [covar_samp](#covar_samp)._ ##### `covar_pop` Returns the sample covariance of a set of number pairs. ```sql covar_samp(expression1, expression2) ``` ###### Arguments - **expression1**: First expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression2**: Second expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT covar_samp(column1, column2) FROM table_name; +-----------------------------------+ | covar_samp(column1, column2) | +-----------------------------------+ | 8.25 | +-----------------------------------+ ``` ##### `covar_samp` Returns the sample covariance of a set of number pairs. ```sql covar_samp(expression1, expression2) ``` ###### Arguments - **expression1**: First expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression2**: Second expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT covar_samp(column1, column2) FROM table_name; +-----------------------------------+ | covar_samp(column1, column2) | +-----------------------------------+ | 8.25 | +-----------------------------------+ ``` ###### Aliases - covar ##### `nth_value` Returns the nth value in a group of values. ```sql nth_value(expression, n ORDER BY expression) ``` ###### Arguments - **expression**: The column or expression to retrieve the nth value from. - **n**: The position (nth) of the value to retrieve, based on the ordering. ###### Example ```sql > SELECT dept_id, salary, NTH_VALUE(salary, 2) OVER (PARTITION BY dept_id ORDER BY salary ASC) AS second_salary_by_dept FROM employee; +---------+--------+-------------------------+ | dept_id | salary | second_salary_by_dept | +---------+--------+-------------------------+ | 1 | 30000 | NULL | | 1 | 40000 | 40000 | | 1 | 50000 | 40000 | | 2 | 35000 | NULL | | 2 | 45000 | 45000 | +---------+--------+-------------------------+ ``` ##### `regr_avgx` Computes the average of the independent variable (input) expression_x for the non-null paired data points. ```sql regr_avgx(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table daily_sales(day int, total_sales int) as values (1,100), (2,150), (3,200), (4,NULL), (5,250); select * from daily_sales; +-----+-------------+ | day | total_sales | | --- | ----------- | | 1 | 100 | | 2 | 150 | | 3 | 200 | | 4 | NULL | | 5 | 250 | +-----+-------------+ SELECT regr_avgx(total_sales, day) AS avg_day FROM daily_sales; +----------+ | avg_day | +----------+ | 2.75 | +----------+ ``` ##### `regr_avgy` Computes the average of the dependent variable (output) expression_y for the non-null paired data points. ```sql regr_avgy(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table daily_temperature(day int, temperature int) as values (1,30), (2,32), (3, NULL), (4,35), (5,36); select * from daily_temperature; +-----+-------------+ | day | temperature | | --- | ----------- | | 1 | 30 | | 2 | 32 | | 3 | NULL | | 4 | 35 | | 5 | 36 | +-----+-------------+ -- temperature as Dependent Variable(Y), day as Independent Variable(X) SELECT regr_avgy(temperature, day) AS avg_temperature FROM daily_temperature; +-----------------+ | avg_temperature | +-----------------+ | 33.25 | +-----------------+ ``` ##### `regr_count` Counts the number of non-null paired data points. ```sql regr_count(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table daily_metrics(day int, user_signups int) as values (1,100), (2,120), (3, NULL), (4,110), (5,NULL); select * from daily_metrics; +-----+---------------+ | day | user_signups | | --- | ------------- | | 1 | 100 | | 2 | 120 | | 3 | NULL | | 4 | 110 | | 5 | NULL | +-----+---------------+ SELECT regr_count(user_signups, day) AS valid_pairs FROM daily_metrics; +-------------+ | valid_pairs | +-------------+ | 3 | +-------------+ ``` ##### `regr_intercept` Computes the y-intercept of the linear regression line. For the equation (y = kx + b), this function returns b. ```sql regr_intercept(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table weekly_performance(week int, productivity_score int) as values (1,60), (2,65), (3, 70), (4,75), (5,80); select * from weekly_performance; +------+---------------------+ | week | productivity_score | | ---- | ------------------- | | 1 | 60 | | 2 | 65 | | 3 | 70 | | 4 | 75 | | 5 | 80 | +------+---------------------+ SELECT regr_intercept(productivity_score, week) AS intercept FROM weekly_performance; +----------+ |intercept| |intercept | +----------+ | 55 | +----------+ ``` ##### `regr_r2` Computes the square of the correlation coefficient between the independent and dependent variables. ```sql regr_r2(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table weekly_performance(day int ,user_signups int) as values (1,60), (2,65), (3, 70), (4,75), (5,80); select * from weekly_performance; +-----+--------------+ | day | user_signups | +-----+--------------+ | 1 | 60 | | 2 | 65 | | 3 | 70 | | 4 | 75 | | 5 | 80 | +-----+--------------+ SELECT regr_r2(user_signups, day) AS r_squared FROM weekly_performance; +---------+ |r_squared| +---------+ | 1.0 | +---------+ ``` ##### `regr_slope` Returns the slope of the linear regression line for non-null pairs in aggregate columns. Given input column Y and X: regr_slope(Y, X) returns the slope (k in Y = k\*X + b) using minimal RSS fitting. ```sql regr_slope(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table weekly_performance(day int, user_signups int) as values (1,60), (2,65), (3, 70), (4,75), (5,80); select * from weekly_performance; +-----+--------------+ | day | user_signups | +-----+--------------+ | 1 | 60 | | 2 | 65 | | 3 | 70 | | 4 | 75 | | 5 | 80 | +-----+--------------+ SELECT regr_slope(user_signups, day) AS slope FROM weekly_performance; +--------+ | slope | +--------+ | 5.0 | +--------+ ``` ##### `regr_sxx` Computes the sum of squares of the independent variable. ```sql regr_sxx(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table study_hours(student_id int, hours int, test_score int) as values (1,2,55), (2,4,65), (3,6,75), (4,8,85), (5,10,95); select * from study_hours; +------------+-------+------------+ | student_id | hours | test_score | +------------+-------+------------+ | 1 | 2 | 55 | | 2 | 4 | 65 | | 3 | 6 | 75 | | 4 | 8 | 85 | | 5 | 10 | 95 | +------------+-------+------------+ SELECT regr_sxx(test_score, hours) AS sxx FROM study_hours; +------+ | sxx | +------+ | 40.0 | +------+ ``` ##### `regr_sxy` Computes the sum of products of paired data points. ```sql regr_sxy(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table employee_productivity(week int, productivity_score int) as values(1,60), (2,65), (3,70); select * from employee_productivity; +------+--------------------+ | week | productivity_score | +------+--------------------+ | 1 | 60 | | 2 | 65 | | 3 | 70 | +------+--------------------+ SELECT regr_sxy(productivity_score, week) AS sum_product_deviations FROM employee_productivity; +------------------------+ | sum_product_deviations | +------------------------+ | 10.0 | +------------------------+ ``` ##### `regr_syy` Computes the sum of squares of the dependent variable. ```sql regr_syy(expression_y, expression_x) ``` ###### Arguments - **expression_y**: Dependent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. - **expression_x**: Independent variable expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql create table employee_productivity(week int, productivity_score int) as values (1,60), (2,65), (3,70); select * from employee_productivity; +------+--------------------+ | week | productivity_score | +------+--------------------+ | 1 | 60 | | 2 | 65 | | 3 | 70 | +------+--------------------+ SELECT regr_syy(productivity_score, week) AS sum_squares_y FROM employee_productivity; +---------------+ | sum_squares_y | +---------------+ | 50.0 | +---------------+ ``` ##### `stddev` Returns the standard deviation of a set of numbers. ```sql stddev(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT stddev(column_name) FROM table_name; +----------------------+ | stddev(column_name) | +----------------------+ | 12.34 | +----------------------+ ``` ###### Aliases - stddev_samp ##### `stddev_pop` Returns the population standard deviation of a set of numbers. ```sql stddev_pop(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT stddev_pop(column_name) FROM table_name; +--------------------------+ | stddev_pop(column_name) | +--------------------------+ | 10.56 | +--------------------------+ ``` ##### `stddev_samp` _Alias of [stddev](#stddev)._ ### Approximate Functions - [approx_distinct](#approx_distinct) - [approx_median](#approx_median) - [approx_percentile_cont](#approx_percentile_cont) - [approx_percentile_cont_with_weight](#approx_percentile_cont_with_weight) ##### `approx_distinct` Returns the approximate number of distinct input values calculated using the HyperLogLog algorithm. ```sql approx_distinct(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT approx_distinct(column_name) FROM table_name; +-----------------------------------+ | approx_distinct(column_name) | +-----------------------------------+ | 42 | +-----------------------------------+ ``` ##### `approx_median` Returns the approximate median (50th percentile) of input values. It is an alias of `approx_percentile_cont(0.5) WITHIN GROUP (ORDER BY x)`. ```sql approx_median(expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. ###### Example ```sql > SELECT approx_median(column_name) FROM table_name; +-----------------------------------+ | approx_median(column_name) | +-----------------------------------+ | 23.5 | +-----------------------------------+ ``` ##### `approx_percentile_cont` Returns the approximate percentile of input values using the t-digest algorithm. ```sql approx_percentile_cont(percentile [, centroids]) WITHIN GROUP (ORDER BY expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. - **percentile**: Percentile to compute. Must be a float value between 0 and 1 (inclusive). - **centroids**: Number of centroids to use in the t-digest algorithm. _Default is 100_. A higher number results in more accurate approximation but requires more memory. ###### Example ```sql > SELECT approx_percentile_cont(0.75) WITHIN GROUP (ORDER BY column_name) FROM table_name; +------------------------------------------------------------------+ | approx_percentile_cont(0.75) WITHIN GROUP (ORDER BY column_name) | +------------------------------------------------------------------+ | 65.0 | +------------------------------------------------------------------+ > SELECT approx_percentile_cont(0.75, 100) WITHIN GROUP (ORDER BY column_name) FROM table_name; +-----------------------------------------------------------------------+ | approx_percentile_cont(0.75, 100) WITHIN GROUP (ORDER BY column_name) | +-----------------------------------------------------------------------+ | 65.0 | +-----------------------------------------------------------------------+ ``` An alternate syntax is also supported: ```sql > SELECT approx_percentile_cont(column_name, 0.75) FROM table_name; +-----------------------------------------------+ | approx_percentile_cont(column_name, 0.75) | +-----------------------------------------------+ | 65.0 | +-----------------------------------------------+ > SELECT approx_percentile_cont(column_name, 0.75, 100) FROM table_name; +----------------------------------------------------------+ | approx_percentile_cont(column_name, 0.75, 100) | +----------------------------------------------------------+ | 65.0 | +----------------------------------------------------------+ ``` ##### `approx_percentile_cont_with_weight` Returns the weighted approximate percentile of input values using the t-digest algorithm. ```sql approx_percentile_cont_with_weight(weight, percentile [, centroids]) WITHIN GROUP (ORDER BY expression) ``` ###### Arguments - **expression**: The expression to operate on. Can be a constant, column, or function, and any combination of operators. - **weight**: Expression to use as weight. Can be a constant, column, or function, and any combination of arithmetic operators. - **percentile**: Percentile to compute. Must be a float value between 0 and 1 (inclusive). - **centroids**: Number of centroids to use in the t-digest algorithm. _Default is 100_. A higher number results in more accurate approximation but requires more memory. ###### Example ```sql > SELECT approx_percentile_cont_with_weight(weight_column, 0.90) WITHIN GROUP (ORDER BY column_name) FROM table_name; +---------------------------------------------------------------------------------------------+ | approx_percentile_cont_with_weight(weight_column, 0.90) WITHIN GROUP (ORDER BY column_name) | +---------------------------------------------------------------------------------------------+ | 78.5 | +---------------------------------------------------------------------------------------------+ > SELECT approx_percentile_cont_with_weight(weight_column, 0.90, 100) WITHIN GROUP (ORDER BY column_name) FROM table_name; +--------------------------------------------------------------------------------------------------+ | approx_percentile_cont_with_weight(weight_column, 0.90, 100) WITHIN GROUP (ORDER BY column_name) | +--------------------------------------------------------------------------------------------------+ | 78.5 | +--------------------------------------------------------------------------------------------------+ ``` An alternative syntax is also supported: ```sql > SELECT approx_percentile_cont_with_weight(column_name, weight_column, 0.90) FROM table_name; +--------------------------------------------------+ | approx_percentile_cont_with_weight(column_name, weight_column, 0.90) | +--------------------------------------------------+ | 78.5 | +--------------------------------------------------+ ``` ## Window Functions A _window function_ performs a calculation across a set of table rows that are somehow related to the current row. This is comparable to the type of calculation that can be done with an aggregate function. However, window functions do not cause rows to become grouped into a single output row like non-window aggregate calls would. Instead, the rows retain their separate identities. Behind the scenes, the window function is able to access more than just the current row of the query result Here is an example that shows how to compare each employee's salary with the average salary in his or her department: ```sql SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary; +-----------+-------+--------+-------------------+ | depname | empno | salary | avg | +-----------+-------+--------+-------------------+ | personnel | 2 | 3900 | 3700.0 | | personnel | 5 | 3500 | 3700.0 | | develop | 8 | 6000 | 5020.0 | | develop | 10 | 5200 | 5020.0 | | develop | 11 | 5200 | 5020.0 | | develop | 9 | 4500 | 5020.0 | | develop | 7 | 4200 | 5020.0 | | sales | 1 | 5000 | 4866.666666666667 | | sales | 4 | 4800 | 4866.666666666667 | | sales | 3 | 4800 | 4866.666666666667 | +-----------+-------+--------+-------------------+ ``` A window function call always contains an OVER clause directly following the window function's name and argument(s). This is what syntactically distinguishes it from a normal function or non-window aggregate. The OVER clause determines exactly how the rows of the query are split up for processing by the window function. The PARTITION BY clause within OVER divides the rows into groups, or partitions, that share the same values of the PARTITION BY expression(s). For each row, the window function is computed across the rows that fall into the same partition as the current row. The previous example showed how to count the average of a column per partition. You can also control the order in which rows are processed by window functions using ORDER BY within OVER. (The window ORDER BY does not even have to match the order in which the rows are output.) Here is an example: ```sql SELECT depname, empno, salary, rank() OVER (PARTITION BY depname ORDER BY salary DESC) FROM empsalary; +-----------+-------+--------+--------+ | depname | empno | salary | rank | +-----------+-------+--------+--------+ | personnel | 2 | 3900 | 1 | | develop | 8 | 6000 | 1 | | develop | 10 | 5200 | 2 | | develop | 11 | 5200 | 2 | | develop | 9 | 4500 | 4 | | develop | 7 | 4200 | 5 | | sales | 1 | 5000 | 1 | | sales | 4 | 4800 | 2 | | personnel | 5 | 3500 | 2 | | sales | 3 | 4800 | 2 | +-----------+-------+--------+--------+ ``` There is another important concept associated with window functions: for each row, there is a set of rows within its partition called its window frame. Some window functions act only on the rows of the window frame, rather than of the whole partition. Here is an example of using window frames in queries: ```sql SELECT depname, empno, salary, avg(salary) OVER(ORDER BY salary ASC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS avg, min(salary) OVER(ORDER BY empno ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum_min FROM empsalary ORDER BY empno ASC; +-----------+-------+--------+--------------------+---------+ | depname | empno | salary | avg | cum_min | +-----------+-------+--------+--------------------+---------+ | sales | 1 | 5000 | 5000.0 | 5000 | | personnel | 2 | 3900 | 3866.6666666666665 | 3900 | | sales | 3 | 4800 | 4700.0 | 3900 | | sales | 4 | 4800 | 4866.666666666667 | 3900 | | personnel | 5 | 3500 | 3700.0 | 3500 | | develop | 7 | 4200 | 4200.0 | 3500 | | develop | 8 | 6000 | 5600.0 | 3500 | | develop | 9 | 4500 | 4500.0 | 3500 | | develop | 10 | 5200 | 5133.333333333333 | 3500 | | develop | 11 | 5200 | 5466.666666666667 | 3500 | +-----------+-------+--------+--------------------+---------+ ``` When a query involves multiple window functions, it is possible to write out each one with a separate OVER clause, but this is duplicative and error-prone if the same windowing behavior is wanted for several functions. Instead, each windowing behavior can be named in a WINDOW clause and then referenced in OVER. For example: ```sql SELECT sum(salary) OVER w, avg(salary) OVER w FROM empsalary WINDOW w AS (PARTITION BY depname ORDER BY salary DESC); ``` ### Syntax The syntax for the OVER-clause is ```sql function([expr]) OVER( [PARTITION BY expr[, …]] [ORDER BY expr [ ASC | DESC ][, …]] [ frame_clause ] ) ``` where **frame_clause** is one of: ```sql { RANGE | ROWS | GROUPS } frame_start { RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end ``` and **frame_start** and **frame_end** can be one of ```sql UNBOUNDED PRECEDING offset PRECEDING CURRENT ROW offset FOLLOWING UNBOUNDED FOLLOWING ``` where **offset** is an non-negative integer. RANGE and GROUPS modes require an ORDER BY clause (with RANGE the ORDER BY must specify exactly one column). ### Filter clause for aggregate window functions Aggregate window functions support the SQL `FILTER (WHERE ...)` clause to include only rows that satisfy the predicate from the window frame in the aggregation. ```sql sum(salary) FILTER (WHERE salary > 0) OVER (PARTITION BY depname ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) ``` If no rows in the frame satisfy the filter for a given output row, `COUNT` yields `0` while `SUM`/`AVG`/`MIN`/`MAX` yield `NULL`. ### Aggregate functions All [aggregate functions](#aggregate-functions) can be used as window functions. ### Ranking Functions - [cume_dist](#cume_dist) - [dense_rank](#dense_rank) - [ntile](#ntile) - [percent_rank](#percent_rank) - [rank](#rank) - [row_number](#row_number) ##### `cume_dist` Relative rank of the current row: (number of rows preceding or peer with the current row) / (total rows). ```sql cume_dist() ``` ###### Example ```sql -- Example usage of the cume_dist window function: SELECT salary, cume_dist() OVER (ORDER BY salary) AS cume_dist FROM employees; +--------+-----------+ | salary | cume_dist | +--------+-----------+ | 30000 | 0.33 | | 50000 | 0.67 | | 70000 | 1.00 | +--------+-----------+ ``` ##### `dense_rank` Returns the rank of the current row without gaps. This function ranks rows in a dense manner, meaning consecutive ranks are assigned even for identical values. ```sql dense_rank() ``` ###### Example ```sql -- Example usage of the dense_rank window function: SELECT department, salary, dense_rank() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank FROM employees; +-------------+--------+------------+ | department | salary | dense_rank | +-------------+--------+------------+ | Sales | 70000 | 1 | | Sales | 50000 | 2 | | Sales | 50000 | 2 | | Sales | 30000 | 3 | | Engineering | 90000 | 1 | | Engineering | 80000 | 2 | +-------------+--------+------------+ ``` ##### `ntile` Integer ranging from 1 to the argument value, dividing the partition as equally as possible ```sql ntile(expression) ``` ###### Arguments - **expression**: An integer describing the number groups the partition should be split into ###### Example ```sql -- Example usage of the ntile window function: SELECT employee_id, salary, ntile(4) OVER (ORDER BY salary DESC) AS quartile FROM employees; +-------------+--------+----------+ | employee_id | salary | quartile | +-------------+--------+----------+ | 1 | 90000 | 1 | | 2 | 85000 | 1 | | 3 | 80000 | 2 | | 4 | 70000 | 2 | | 5 | 60000 | 3 | | 6 | 50000 | 3 | | 7 | 40000 | 4 | | 8 | 30000 | 4 | +-------------+--------+----------+ ``` ##### `percent_rank` Returns the percentage rank of the current row within its partition. The value ranges from 0 to 1 and is computed as `(rank - 1) / (total_rows - 1)`. ```sql percent_rank() ``` ###### Example ```sql -- Example usage of the percent_rank window function: SELECT employee_id, salary, percent_rank() OVER (ORDER BY salary) AS percent_rank FROM employees; +-------------+--------+---------------+ | employee_id | salary | percent_rank | +-------------+--------+---------------+ | 1 | 30000 | 0.00 | | 2 | 50000 | 0.50 | | 3 | 70000 | 1.00 | +-------------+--------+---------------+ ``` ##### `rank` Returns the rank of the current row within its partition, allowing gaps between ranks. This function provides a ranking similar to `row_number`, but skips ranks for identical values. ```sql rank() ``` ###### Example ```sql -- Example usage of the rank window function: SELECT department, salary, rank() OVER (PARTITION BY department ORDER BY salary DESC) AS rank FROM employees; +-------------+--------+------+ | department | salary | rank | +-------------+--------+------+ | Sales | 70000 | 1 | | Sales | 50000 | 2 | | Sales | 50000 | 2 | | Sales | 30000 | 4 | | Engineering | 90000 | 1 | | Engineering | 80000 | 2 | +-------------+--------+------+ ``` ##### `row_number` Number of the current row within its partition, counting from 1. ```sql row_number() ``` ###### Example ```sql -- Example usage of the row_number window function: SELECT department, salary, row_number() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num FROM employees; +-------------+--------+---------+ | department | salary | row_num | +-------------+--------+---------+ | Sales | 70000 | 1 | | Sales | 50000 | 2 | | Sales | 50000 | 3 | | Sales | 30000 | 4 | | Engineering | 90000 | 1 | | Engineering | 80000 | 2 | +-------------+--------+---------+ ``` ### Analytical Functions - [first_value](#first_value) - [lag](#lag) - [last_value](#last_value) - [lead](#lead) - [nth_value](#nth_value) ##### `first_value` Returns value evaluated at the row that is the first row of the window frame. ```sql first_value(expression) ``` ###### Arguments - **expression**: Expression to operate on ###### Example ```sql -- Example usage of the first_value window function: SELECT department, employee_id, salary, first_value(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS top_salary FROM employees; +-------------+-------------+--------+------------+ | department | employee_id | salary | top_salary | +-------------+-------------+--------+------------+ | Sales | 1 | 70000 | 70000 | | Sales | 2 | 50000 | 70000 | | Sales | 3 | 30000 | 70000 | | Engineering | 4 | 90000 | 90000 | | Engineering | 5 | 80000 | 90000 | +-------------+-------------+--------+------------+ ``` ##### `lag` Returns value evaluated at the row that is offset rows before the current row within the partition; if there is no such row, instead return default (which must be of the same type as value). ```sql lag(expression, offset, default) ``` ###### Arguments - **expression**: Expression to operate on - **offset**: Integer. Specifies how many rows back the value of expression should be retrieved. Defaults to 1. - **default**: The default value if the offset is not within the partition. Must be of the same type as expression. ###### Example ```sql -- Example usage of the lag window function: SELECT employee_id, salary, lag(salary, 1, 0) OVER (ORDER BY employee_id) AS prev_salary FROM employees; +-------------+--------+-------------+ | employee_id | salary | prev_salary | +-------------+--------+-------------+ | 1 | 30000 | 0 | | 2 | 50000 | 30000 | | 3 | 70000 | 50000 | | 4 | 60000 | 70000 | +-------------+--------+-------------+ ``` ##### `last_value` Returns value evaluated at the row that is the last row of the window frame. ```sql last_value(expression) ``` ###### Arguments - **expression**: Expression to operate on ###### Example ```sql -- SQL example of last_value: SELECT department, employee_id, salary, last_value(salary) OVER (PARTITION BY department ORDER BY salary) AS running_last_salary FROM employees; +-------------+-------------+--------+---------------------+ | department | employee_id | salary | running_last_salary | +-------------+-------------+--------+---------------------+ | Sales | 1 | 30000 | 30000 | | Sales | 2 | 50000 | 50000 | | Sales | 3 | 70000 | 70000 | | Engineering | 4 | 40000 | 40000 | | Engineering | 5 | 60000 | 60000 | +-------------+-------------+--------+---------------------+ ``` ##### `lead` Returns value evaluated at the row that is offset rows after the current row within the partition; if there is no such row, instead return default (which must be of the same type as value). ```sql lead(expression, offset, default) ``` ###### Arguments - **expression**: Expression to operate on - **offset**: Integer. Specifies how many rows forward the value of expression should be retrieved. Defaults to 1. - **default**: The default value if the offset is not within the partition. Must be of the same type as expression. ###### Example ```sql -- Example usage of lead window function: SELECT employee_id, department, salary, lead(salary, 1, 0) OVER (PARTITION BY department ORDER BY salary) AS next_salary FROM employees; +-------------+-------------+--------+--------------+ | employee_id | department | salary | next_salary | +-------------+-------------+--------+--------------+ | 1 | Sales | 30000 | 50000 | | 2 | Sales | 50000 | 70000 | | 3 | Sales | 70000 | 0 | | 4 | Engineering | 40000 | 60000 | | 5 | Engineering | 60000 | 0 | +-------------+-------------+--------+--------------+ ``` ##### `nth_value` Returns the value evaluated at the nth row of the window frame (counting from 1). Returns NULL if no such row exists. ```sql nth_value(expression, n) ``` ###### Arguments - **expression**: The column from which to retrieve the nth value. - **n**: Integer. Specifies the row number (starting from 1) in the window frame. ###### Example ```sql -- Sample employees table: CREATE TABLE employees (id INT, salary INT); INSERT INTO employees (id, salary) VALUES (1, 30000), (2, 40000), (3, 50000), (4, 60000), (5, 70000); -- Example usage of nth_value: SELECT nth_value(salary, 2) OVER ( ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS nth_value FROM employees; +-----------+ | nth_value | +-----------+ | 40000 | | 40000 | | 40000 | | 40000 | | 40000 | +-----------+ ``` ## Special Functions ### Expansion Functions - [unnest](#unnest) - [unnest(struct)](#unnest-struct) ##### `unnest` Expands an array or map into rows. ###### Arguments - **array**: Array expression to unnest. Can be a constant, column, or function, and any combination of array operators. ###### Examples ```sql > select unnest(make_array(1, 2, 3, 4, 5)) as unnested; +----------+ | unnested | +----------+ | 1 | | 2 | | 3 | | 4 | | 5 | +----------+ ``` ```sql > select unnest(range(0, 10)) as unnested_range; +----------------+ | unnested_range | +----------------+ | 0 | | 1 | | 2 | | 3 | | 4 | | 5 | | 6 | | 7 | | 8 | | 9 | +----------------+ ``` ##### `unnest (struct)` Expand a struct fields into individual columns. Each field of the struct will be prefixed with `__unnest_placeholder` and could be accessed via `"__unnest_placeholder()."`. ###### Arguments - **struct**: Object expression to unnest. Can be a constant, column, or function, and any combination of object operators. ###### Examples ```sql > create table foo as values ({a: 5, b: 'a string'}), ({a:6, b: 'another string'}); > create view foov as select column1 as struct_column from foo; > select * from foov; +---------------------------+ | struct_column | +---------------------------+ | {a: 5, b: a string} | | {a: 6, b: another string} | +---------------------------+ > select unnest(struct_column) from foov; +--------------------------------------------+--------------------------------------------+ | __unnest_placeholder(foov.struct_column).a | __unnest_placeholder(foov.struct_column).b | +--------------------------------------------+--------------------------------------------+ | 5 | a string | | 6 | another string | +--------------------------------------------+--------------------------------------------+ ``` --- ## Geospatial Functions This page lists all geospatial related functions in GreptimeDB. These functions are enabled when you have `common-function/geo` feature turned on (default: on). ## Geo Types Convert data between geospatial types. ### `wkt_point_from_latlng` Convert latitude and longitude to WKT point. ```sql SELECT wkt_point_from_latlng(37.76938, -122.3889) AS point; ``` ## Geohash See [more about geohash encoding algorithms](https://en.wikipedia.org/wiki/Geohash). ### `geohash` Get geohash encoded string from latitude, longitude and resolution. ```sql SELECT geohash(37.76938, -122.3889, 11); ``` ### `geohash_neighbours` Get all geohash neighbours for given coordinate and resolution. Note that this function returns a String array and it only works on our HTTP Query API and Postgres channel. ```sql SELECT geohash_neighbours(37.76938, -122.3889, 11); ``` ## H3 H3 is a geospatial index algorithm. See [more about h3 encoding](https://h3geo.org/). ### `h3_latlng_to_cell` Encode coordinate (latitude, longitude) into h3 index at given resolution. Returns UInt64 representation of the cell. ```sql SELECT h3_latlng_to_cell(37.76938, -122.3889, 1); ``` ### `h3_latlng_to_cell_string` Similar to `h3_latlng_to_cell` but returns hex encoding of the cell. ```sql SELECT h3_latlng_to_cell_string(37.76938, -122.3889, 1); ``` ### `h3_cell_to_string` Convert cell index (UInt64) to its string representation. ```sql SELECT h3_cell_to_string(h3_latlng_to_cell(37.76938, -122.3889, 8)); ``` ### `h3_string_to_cell` Convert hex-encoded cell ID to its UInt64 form ```sql SELECT h3_string_to_cell(h3_latlng_to_cell_string(37.76938, -122.3889, 8::UInt64)); ``` ### `h3_cell_center_latlng` Returns cell centroid as latitude and longitude. Note that this function returns a Float array and it only works on our HTTP Query API and Postgres channel. ```sql SELECT h3_cell_center_latlng(h3_latlng_to_cell(37.76938, -122.3889, 8)); ``` ### `h3_cell_resolution` Returns resolution for given cell. ```sql SELECT h3_cell_resolution(h3_latlng_to_cell(37.76938, -122.3889, 8)); ``` ### `h3_cell_base` Returns the base cell of given cell. ```sql SELECT h3_cell_base(h3_latlng_to_cell(37.76938, -122.3889, 8)); ``` ### `h3_cell_is_pentagon` Returns true if cell is a pentagon. ```sql SELECT h3_cell_is_pentagon(h3_latlng_to_cell(37.76938, -122.3889, 8)); ``` ### `h3_cell_parent` Returns parent cell at given resolution. ```sql SELECT h3_cell_parent(h3_latlng_to_cell(37.76938, -122.3889, 8), 6); ``` ### `h3_cell_center_child` Returns the center child of a cell at the requested resolution. The target resolution must be greater than or equal to the input cell's resolution. ```sql SELECT h3_cell_center_child(h3_latlng_to_cell(37.76938, -122.3889, 8), 10); ``` ### `h3_cell_to_children` Returns children cells at given resolution. Note that this function returns a UInt64 array and it only works on our HTTP Query API and Postgres channel. ```sql SELECT h3_cell_to_children(h3_latlng_to_cell(37.76938, -122.3889, 8), 10); ``` ### `h3_cell_to_children_size` Returns cell children count at given resolution. ```sql SELECT h3_cell_to_children_size(h3_latlng_to_cell(37.76938, -122.3889, 8), 10); ``` ### `h3_cell_to_child_pos` Return the child position for its parent at given resolution. Position is the index of child in its children. ```sql SELECT h3_cell_to_child_pos(h3_latlng_to_cell(37.76938, -122.3889, 8), 6) ``` ### `h3_child_pos_to_cell` Returns the cell at given position of the parent at given resolution. Arguments: - position - cell index - resolution ```sql SELECT h3_child_pos_to_cell(25, h3_latlng_to_cell(37.76938, -122.3889, 8), 11); ``` ### `h3_cells_contains` Return true if given cells contains cell, or cell's parent. Arguments: - cell set: it can be int64/uint64/string array, and comma-separated string cell ID. - cell index: the cell to test ```sql SELECT h3_cells_contains('86283470fffffff,862834777ffffff, 862834757ffffff, 86283471fffffff, 862834707ffffff', '8b283470d112fff') AS R00, h3_cells_contains(['86283470fffffff', '862834777ffffff', '862834757ffffff', '86283471fffffff', '862834707ffffff'], '8b283470d112fff') AS R11, h3_cells_contains([604189641255419903, 604189643000250367, 604189642463379455, 604189641523855359, 604189641121202175], 604189641792290815) AS R21; ``` ### `h3_grid_disk` Returns cells with k distances of given cell. Note that this function returns a UInt64 array and it only works on our HTTP Query API and Postgres channel. ```sql SELECT h3_grid_disk(h3_latlng_to_cell(37.76938, -122.3889, 8), 3); ``` ### `h3_grid_disk_distances` Returns all cells **within** k distances of given cell. Note that this function returns a UInt64 array and it only works on our HTTP Query API and Postgres channel. ```sql SELECT h3_grid_disk_distances(h3_latlng_to_cell(37.76938, -122.3889, 8), 3); ``` ### `h3_grid_distance` Returns distance between two cells. ```sql SELECT h3_grid_distance(cell1, cell2) AS distance, FROM ( SELECT h3_latlng_to_cell(37.76938, -122.3889, 8) AS cell1, h3_latlng_to_cell(39.634, -104.999, 8) AS cell2 ); ``` ### `h3_grid_path_cells` Returns path cells between two cells. Note that this function can fail if two cells are very far apart. ```sql SELECT h3_grid_path_cells(cell1, cell2) AS path_cells, FROM ( SELECT h3_latlng_to_cell(37.76938, -122.3889, 8) AS cell1, h3_latlng_to_cell(39.634, -104.999, 8) AS cell2 ); ``` ### `h3_distance_sphere_km` Returns sphere distance between centroid of two cells, in km ```sql SELECT round(h3_distance_sphere_km(cell1, cell2), 5) AS sphere_distance FROM ( SELECT h3_string_to_cell('86283082fffffff') AS cell1, h3_string_to_cell('86283470fffffff') AS cell2 ); ``` ### `h3_distance_degree` Returns euclidean distance between centroid of two cells, in degree. ```sql SELECT round(h3_distance_degree(cell1, cell2), 14) AS euclidean_distance FROM ( SELECT h3_string_to_cell('86283082fffffff') AS cell1, h3_string_to_cell('86283470fffffff') AS cell2 ); ``` ## S2 Learn [more about S2 encoding](http://s2geometry.io/). ### `s2_latlng_to_cell` Returns s2 cell index of given coordinate. ```sql SELECT s2_latlng_to_cell(37.76938, -122.3889); ``` ### `s2_cell_to_token` Returns string representation of given cell. ```sql SELECT s2_cell_to_token(s2_latlng_to_cell(37.76938, -122.3889)); ``` ### `s2_cell_level` Returns s2 level of given cell. ```sql SELECT s2_cell_level(s2_latlng_to_cell(37.76938, -122.3889)); ``` ### `s2_cell_parent` Returns parent of given cell at level. ```sql SELECT s2_cell_parent(s2_latlng_to_cell(37.76938, -122.3889), 3); ``` ## Encodings ### `geo_path` Aggregates latitude and longitude columns into a struct containing `lat` and `lng` arrays. The coordinates are ordered by the timestamp argument. Arguments: - latitude as `Float64` - longitude as `Float64` - timestamp ```sql SELECT geo_path(lat, lon, ts) FROM positions; ``` ### `json_encode_path` Encode rows of latitude, longitude in a JSON array, sorted by timestamp. Arguments: - latitude - longitude - timestamp Returns a JSON array in string type. Note that the sequence in result coordinate is [longitude, latitude] to align with GeoJSON spec. ```sql SELECT json_encode_path(lat, lon, ts); ``` Example output: ```json [[-122.3888,37.77001],[-122.3839,37.76928],[-122.3889,37.76938],[-122.382,37.7693]] ``` ## Spatial Relation ### `st_contains` Test spatial relationship between two objects: contains. Arguments: two spatial objects encoded with WKT. ```sql SELECT st_contains(polygon1, p1), st_contains(polygon2, p1), FROM ( SELECT wkt_point_from_latlng(37.383287, -122.01325) AS p1, 'POLYGON ((-122.031661 37.428252, -122.139829 37.387072, -122.135365 37.361971, -122.057759 37.332222, -121.987707 37.328946, -121.943754 37.333041, -121.919373 37.349145, -121.945814 37.376705, -121.975689 37.417345, -121.998696 37.409164, -122.031661 37.428252))' AS polygon1, 'POLYGON ((-121.491698 38.653343, -121.582353 38.556757, -121.469721 38.449287, -121.315883 38.541721, -121.491698 38.653343))' AS polygon2, ); ``` ### `st_within` Test spatial relationship between two objects: within. Arguments: two spatial objects encoded with WKT. ```sql SELECT st_within(p1, polygon1), st_within(p1, polygon2), FROM ( SELECT wkt_point_from_latlng(37.383287, -122.01325) AS p1, 'POLYGON ((-122.031661 37.428252, -122.139829 37.387072, -122.135365 37.361971, -122.057759 37.332222, -121.987707 37.328946, -121.943754 37.333041, -121.919373 37.349145, -121.945814 37.376705, -121.975689 37.417345, -121.998696 37.409164, -122.031661 37.428252))' AS polygon1, 'POLYGON ((-121.491698 38.653343, -121.582353 38.556757, -121.469721 38.449287, -121.315883 38.541721, -121.491698 38.653343))' AS polygon2, ); ``` ### `st_intersects` Test spatial relationship between two objects: intersects. Arguments: two spatial objects encoded with WKT. ```sql SELECT st_intersects(polygon1, polygon2), st_intersects(polygon1, polygon3), FROM ( SELECT 'POLYGON ((-122.031661 37.428252, -122.139829 37.387072, -122.135365 37.361971, -122.057759 37.332222, -121.987707 37.328946, -121.943754 37.333041, -121.919373 37.349145, -121.945814 37.376705, -121.975689 37.417345, -121.998696 37.409164, -122.031661 37.428252))' AS polygon1, 'POLYGON ((-121.491698 38.653343, -121.582353 38.556757, -121.469721 38.449287, -121.315883 38.541721, -121.491698 38.653343))' AS polygon2, 'POLYGON ((-122.089628 37.450332, -122.20535 37.378342, -122.093062 37.36088, -122.044301 37.372886, -122.089628 37.450332))' AS polygon3, ); ``` ## Spatial Measurement ### `st_distance` Returns WGS84(SRID: 4326) euclidean distance between two geometry object, in degree. Arguments: two spatial objects encoded with WKT. ```sql SELECT st_distance(p1, p2) AS euclidean_dist, st_distance(p1, polygon1) AS euclidean_dist_pp, FROM ( SELECT wkt_point_from_latlng(37.76938, -122.3889) AS p1, wkt_point_from_latlng(38.5216, -121.4247) AS p2, 'POLYGON ((-121.491698 38.653343, -121.582353 38.556757, -121.469721 38.449287, -121.315883 38.541721, -121.491698 38.653343))' AS polygon1, ); ``` ### `st_distance_sphere_m` Return great circle distance between two point, in meters. Arguments: two spatial objects encoded with WKT. ```sql SELECT st_distance_sphere_m(p1, p2) AS sphere_dist_m, FROM ( SELECT wkt_point_from_latlng(37.76938, -122.3889) AS p1, wkt_point_from_latlng(38.5216, -121.4247) AS p2, ); ``` ### `st_area` Returns area of given geometry object. Arguments: the spatial object encoded with WKT. ```sql SELECT st_area(p1) as area_point, st_area(polygon1) as area_polygon, FROM ( SELECT wkt_point_from_latlng(37.76938, -122.3889) AS p1, 'POLYGON ((-121.491698 38.653343, -121.582353 38.556757, -121.469721 38.449287, -121.315883 38.541721, -121.491698 38.653343))' AS polygon1, ); ``` --- ## GreptimeDB Functions ## String Functions DataFusion [String Function](./df-functions.md#string-functions). GreptimeDB provides: * `matches_term(expression, term)` for full text search. For details, read the [Fulltext Search](/user-guide/logs/fulltext-search.md). * `regexp_extract(str, regexp)` to extract the first substring in a string that matches a regular expression. Returns `NULL` if no match is found. **MySQL-Compatible String Functions:** GreptimeDB also provides the following MySQL-compatible string functions: * `locate(substr, str[, pos])` - Returns the position of the first occurrence of substring * `elt(N, str1, str2, ...)` - Returns the Nth string from the list * `field(str, str1, str2, ...)` - Returns the index of the first string that matches * `insert(str, pos, len, newstr)` - Inserts a substring at a specified position * `space(N)` - Returns a string of N space characters * `format(X, D)` - Formats a number with thousand separators and D decimal places ### regexp_extract Extracts the first substring in a string that matches a [regular expression](https://docs.rs/regex/latest/regex/#syntax). Returns `NULL` if no match is found. ```sql regexp_extract(str, regexp) ``` **Arguments:** - **str**: String expression to operate on. Can be a constant, column, or function, and any combination of operators. - **regexp**: Regular expression to match against. Can be a constant, column, or function. **Note on Escaping:** GreptimeDB's regex escape behavior differs between MySQL and PostgreSQL compatibility modes: - **MySQL mode**: Requires double backslashes for escape sequences (e.g., `\\d`, `\\s`) - **PostgreSQL mode**: Single backslashes work by default (e.g., `\d`, `\s`), or use `E''` prefix for consistency with MySQL (e.g., `E'\\d'`) **Examples:** ```sql SELECT regexp_extract('version 1.2.3', '\d+\.\d+\.\d+'); -- Returns: 1.2.3 SELECT regexp_extract('Phone: 123-456-7890', '\d{3}-\d{3}-\d{4}'); -- Returns: 123-456-7890 SELECT regexp_extract('no match here', '\d+\.\d+\.\d+'); -- Returns: NULL ``` ### locate Returns the position of the first occurrence of substring `substr` in string `str`. Optionally, you can specify a starting position `pos`. Returns 0 if the substring is not found. ```sql locate(substr, str[, pos]) ``` **Arguments:** - **substr**: The substring to search for. - **str**: The string to search in. - **pos** (optional): The position to start searching from (1-based). If omitted, searching starts from the beginning. **Examples:** ```sql SELECT locate('world', 'hello world'); -- Returns: 7 SELECT locate('o', 'hello world', 6); -- Returns: 8 (finds the second 'o') SELECT locate('xyz', 'hello world'); -- Returns: 0 (not found) ``` ### elt Returns the Nth string from a list of strings. Returns `NULL` if N is less than 1, greater than the number of strings, or `NULL`. ```sql elt(N, str1, str2, str3, ...) ``` **Arguments:** - **N**: The index of the string to return (1-based). - **str1, str2, str3, ...**: The list of strings. **Examples:** ```sql SELECT elt(2, 'apple', 'banana', 'cherry'); -- Returns: banana SELECT elt(0, 'apple', 'banana', 'cherry'); -- Returns: NULL ``` ### field Returns the index (1-based) of the first string that matches `str` in the list. Returns 0 if no match is found or if `str` is `NULL`. ```sql field(str, str1, str2, str3, ...) ``` **Arguments:** - **str**: The string to search for. - **str1, str2, str3, ...**: The list of strings to search in. **Examples:** ```sql SELECT field('banana', 'apple', 'banana', 'cherry'); -- Returns: 2 SELECT field('grape', 'apple', 'banana', 'cherry'); -- Returns: 0 (not found) ``` ### insert Inserts a substring into a string at a specified position, replacing a specified number of characters. ```sql insert(str, pos, len, newstr) ``` **Arguments:** - **str**: The original string. - **pos**: The position to start inserting (1-based). - **len**: The number of characters to replace. - **newstr**: The string to insert. **Examples:** ```sql SELECT insert('Quadratic', 3, 4, 'What'); -- Returns: QuWhattic SELECT insert('Quadratic', 3, 100, 'What'); -- Returns: QuWhat (replaces to end of string) ``` ### space Returns a string consisting of N space characters. ```sql space(N) ``` **Arguments:** - **N**: The number of spaces to return. Returns empty string if N is negative. **Examples:** ```sql SELECT space(5); -- Returns: ' ' (5 spaces) SELECT concat('hello', space(3), 'world'); -- Returns: 'hello world' ``` ### format Formats a number with thousand separators and a specified number of decimal places. ```sql format(X, D) ``` **Arguments:** - **X**: The number to format. - **D**: The number of decimal places (0-30). **Examples:** ```sql SELECT format(1234567.891, 2); -- Returns: 1,234,567.89 SELECT format(1234567.891, 0); -- Returns: 1,234,568 ``` ## Math Functions DataFusion [Math Function](./df-functions.md#math-functions). GreptimeDB provides: ### clamp * `clamp(value, lower, upper)` to restrict a given value between a lower and upper bound: ```sql SELECT CLAMP(10, 0, 1); +------------------------------------+ | clamp(Int64(10),Int64(0),Int64(1)) | +------------------------------------+ | 1 | +------------------------------------+ ``` ```sql SELECT CLAMP(0.5, 0, 1) +---------------------------------------+ | clamp(Float64(0.5),Int64(0),Int64(1)) | +---------------------------------------+ | 0.5 | +---------------------------------------+ ``` ### mod * `mod(x, y)` to get the remainder of a number divided by another number: ```sql SELECT mod(18, 4); +-------------------------+ | mod(Int64(18),Int64(4)) | +-------------------------+ | 2 | +-------------------------+ ``` ## Date and Time Functions DataFusion [Time and Date Function](./df-functions.md#time-and-date-functions). GreptimeDB provides: * [date_add](#date_add) * [date_sub](#date_sub) * [date_format](#date_format) * [to_unixtime](#to_unixtime) * [timezone](#timezone) ### date_add * `date_add(expression, interval)` to add an interval value to Timestamp, Date, or DateTime ```sql SELECT date_add('2023-12-06'::DATE, '3 month 5 day'); ``` ``` +----------------------------------------------------+ | date_add(Utf8("2023-12-06"),Utf8("3 month 5 day")) | +----------------------------------------------------+ | 2024-03-11 | +----------------------------------------------------+ ``` ### date_sub * `date_sub(expression, interval)` to subtract an interval value to Timestamp, Date, or DateTime ```sql SELECT date_sub('2023-12-06 07:39:46.222'::TIMESTAMP_MS, '5 day'::INTERVAL); ``` ``` +-----------------------------------------------------------------------------------------------------------------------------------------+ | date_sub(arrow_cast(Utf8("2023-12-06 07:39:46.222"),Utf8("Timestamp(Millisecond, None)")),IntervalMonthDayNano("92233720368547758080")) | +-----------------------------------------------------------------------------------------------------------------------------------------+ | 2023-12-01 07:39:46.222000 | +-----------------------------------------------------------------------------------------------------------------------------------------+ ``` ### date_format * `date_format(expression, fmt)` to format Timestamp, Date, or DateTime into string by the format: Supports `Date32`, `Date64`, and all `Timestamp` types. ```sql SELECT date_format('2023-12-06 07:39:46.222'::TIMESTAMP, '%Y-%m-%d %H:%M:%S:%3f'); ``` ``` +-----------------------------------------------------------------------------------------------------------------------------+ | date_format(arrow_cast(Utf8("2023-12-06 07:39:46.222"),Utf8("Timestamp(Millisecond, None)")),Utf8("%Y-%m-%d %H:%M:%S:%3f")) | +-----------------------------------------------------------------------------------------------------------------------------+ | 2023-12-06 07:39:46:222 | +-----------------------------------------------------------------------------------------------------------------------------+ ``` Supported specifiers refer to the [chrono::format::strftime](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) module. ### to_unixtime * `to_unixtime(expression)` to convert the expression into the Unix timestamp in seconds. The argument can be integers (Unix timestamp in milliseconds), Timestamp, Date, DateTime, or String. If the argument is the string type, the function will first try to convert it into a DateTime, Timestamp, or Date. ```sql select to_unixtime('2023-03-01T06:35:02Z'); ``` ``` +-------------------------------------------+ | to_unixtime(Utf8("2023-03-01T06:35:02Z")) | +-------------------------------------------+ | 1677652502 | +-------------------------------------------+ ``` ```sql select to_unixtime('2023-03-01'::date); ``` ``` +---------------------------------+ | to_unixtime(Utf8("2023-03-01")) | +---------------------------------+ | 1677628800 | +---------------------------------+ ``` ### timezone * `timezone()` to retrieve the current session timezone: ```sql select timezone(); ``` ``` +------------+ | timezone() | +------------+ | UTC | +------------+ ``` ## System Functions * `isnull(expression)` to check whether an expression is `NULL`: ```sql SELECT isnull(1); +------------------+ | isnull(Int64(1)) | +------------------+ | 0 | +------------------+ ``` ```sql SELECT isnull(NULL); +--------------+ | isnull(NULL) | +--------------+ | 1 | +--------------+ ``` * `build()` retrieves the GreptimeDB build info. * `version()` retrieves the GreptimeDB version. * `database()` retrieves the current session database: ```sql select database(); +------------+ | database() | +------------+ | public | +------------+ ``` ## Admin Functions GreptimeDB provides `ADMIN` statement to run the administration functions, please refer to [ADMIN](/reference/sql/admin.md) reference. --- ## IP Functions This document describes the IP address manipulation and comparison functions available. ### `ipv4_to_cidr(ip_string [, subnet_mask])` **Description:** Converts an IPv4 address string to CIDR notation. If a `subnet_mask` (UInt8, 0-32) is provided, it uses that mask. Otherwise, it automatically detects the subnet based on trailing zeros or the number of octets provided in the input string (e.g., '192.168' implies /16, '192' implies /8). The resulting IP address in the CIDR notation will have host bits zeroed out according to the mask. **Arguments:** * `ip_string`: String - The IPv4 address string (e.g., '192.168.1.1', '10.0.0.0', '172.16'). Partial addresses are completed with zeros. * `subnet_mask` (Optional): UInt8 - The desired subnet mask length (e.g., 24, 16, 8). **Return Type:** String - The IPv4 address in CIDR notation (e.g., '192.168.1.0/24'). Returns NULL for invalid inputs. **Examples:** ```sql -- Auto-detect subnet SELECT ipv4_to_cidr('192.168.1.0'); -- Output: '192.168.1.0/24' SELECT ipv4_to_cidr('172.16'); -- Output: '172.16.0.0/16' -- Explicit subnet mask SELECT ipv4_to_cidr('192.168.1.1', 24); -- Output: '192.168.1.0/24' SELECT ipv4_to_cidr('10.0.0.1', 16); -- Output: '10.0.0.0/16' ``` ### `ipv6_to_cidr(ip_string [, subnet_mask])` **Description:** Converts an IPv6 address string to CIDR notation. If a `subnet_mask` (UInt8, 0-128) is provided, it uses that mask. Otherwise, it attempts to automatically detect the subnet based on trailing zero segments or common prefixes (like `fe80::` for /16 or `2001:db8::` for /32). The resulting IP address in the CIDR notation will have host bits zeroed out according to the mask. **Arguments:** * `ip_string`: String - The IPv6 address string (e.g., '2001:db8::1', 'fe80::'). Partial addresses are completed if possible (e.g., '2001:db8' becomes '2001:db8::'). * `subnet_mask` (Optional): UInt8 - The desired subnet mask length (e.g., 48, 64, 128). **Return Type:** String - The IPv6 address in CIDR notation (e.g., '2001:db8::/32'). Returns NULL for invalid inputs. **Examples:** ```sql -- Auto-detect subnet SELECT ipv6_to_cidr('2001:db8::'); -- Output: '2001:db8::/32' SELECT ipv6_to_cidr('fe80::1'); -- Output: 'fe80::/16' SELECT ipv6_to_cidr('::1'); -- Output: '::1/128' -- Explicit subnet mask SELECT ipv6_to_cidr('2001:db8::', 48); -- Output: '2001:db8::/48' SELECT ipv6_to_cidr('fe80::1', 10); -- Output: 'fe80::/10' ``` ### `ipv4_num_to_string(ip_number)` **Description:** Converts a UInt32 number to an IPv4 address string in the standard A.B.C.D format. The number is interpreted as an IPv4 address in big-endian byte order. **Arguments:** * `ip_number`: UInt32 - The numeric representation of the IPv4 address. **Return Type:** String - The IPv4 address in dot-decimal notation (e.g., '192.168.0.1'). **Examples:** ```sql SELECT ipv4_num_to_string(3232235521::uint32); -- 0xC0A80001 -- Output: '192.168.0.1' SELECT ipv4_num_to_string(167772161::uint32); -- 0x0A000001 -- Output: '10.0.0.1' SELECT ipv4_num_to_string(0::uint32); -- Output: '0.0.0.0' ``` ### `ipv4_string_to_num(ip_string)` **Description:** Converts a string representation of an IPv4 address (A.B.C.D format) to its UInt32 numeric equivalent (big-endian). **Arguments:** * `ip_string`: String - The IPv4 address in dot-decimal notation. **Return Type:** UInt32 - The numeric representation of the IPv4 address. Returns NULL or throws an error for invalid input formats. **Examples:** ```sql SELECT ipv4_string_to_num('192.168.0.1'); -- Output: 3232235521 SELECT ipv4_string_to_num('10.0.0.1'); -- Output: 167772161 SELECT ipv4_string_to_num('0.0.0.0'); -- Output: 0 ``` ### `ipv6_num_to_string(hex_string)` **Description:** Converts a 32-character hexadecimal string representation of an IPv6 address to its standard formatted string representation (e.g., '2001:db8::1'). Handles IPv4-mapped IPv6 addresses correctly (e.g., '::ffff:192.168.0.1'). Case-insensitive for hex characters. **Arguments:** * `hex_string`: String - A 32-character hexadecimal string representing the 16 bytes of an IPv6 address. **Return Type:** String - The formatted IPv6 address string. Returns NULL or throws an error if the input is not a valid 32-character hex string. **Examples:** ```sql SELECT ipv6_num_to_string('20010db8000000000000000000000001'); -- Output: '2001:db8::1' SELECT ipv6_num_to_string('00000000000000000000ffffc0a80001'); -- Output: '::ffff:192.168.0.1' ``` ### `ipv6_string_to_num(ip_string)` **Description:** Converts a string representation of an IPv6 address (standard format) or an IPv4 address (dot-decimal format) to its 16-byte binary representation. If an IPv4 address string is provided, it is converted to its IPv6-mapped equivalent (e.g., '192.168.0.1' becomes '::ffff:192.168.0.1' internally before conversion to binary). **Arguments:** * `ip_string`: String - The IPv6 or IPv4 address string. **Return Type:** Binary - The 16-byte binary representation of the IPv6 address. Returns NULL or throws an error for invalid input formats. **Examples:** ```sql -- IPv6 input SELECT ipv6_string_to_num('2001:db8::1'); -- Output: Binary representation of 2001:db8::1 -- IPv4 input (gets mapped to IPv6) SELECT ipv6_string_to_num('192.168.0.1'); -- Output: Binary representation of ::ffff:192.168.0.1 -- IPv4-mapped IPv6 input SELECT ipv6_string_to_num('::ffff:192.168.0.1'); -- Output: Binary representation of ::ffff:192.168.0.1 ``` ### `ipv4_in_range(ip_string, cidr_string)` **Description:** Checks if a given IPv4 address string falls within a specified CIDR range string. **Arguments:** * `ip_string`: String - The IPv4 address to check (e.g., '192.168.1.5'). * `cidr_string`: String - The CIDR range to check against (e.g., '192.168.1.0/24'). **Return Type:** Boolean - `true` if the IP address is within the range, `false` otherwise. Returns NULL or throws an error for invalid inputs. **Examples:** ```sql SELECT ipv4_in_range('192.168.1.5', '192.168.1.0/24'); -- Output: true SELECT ipv4_in_range('192.168.2.1', '192.168.1.0/24'); -- Output: false SELECT ipv4_in_range('10.0.0.1', '10.0.0.0/8'); -- Output: true SELECT ipv4_in_range('8.8.8.8', '0.0.0.0/0'); -- /0 matches everything -- Output: true SELECT ipv4_in_range('192.168.1.1', '192.168.1.1/32'); -- /32 is an exact match -- Output: true ``` ### `ipv6_in_range(ip_string, cidr_string)` **Description:** Checks if a given IPv6 address string falls within a specified CIDR range string. **Arguments:** * `ip_string`: String - The IPv6 address to check (e.g., '2001:db8::1'). * `cidr_string`: String - The CIDR range to check against (e.g., '2001:db8::/32'). **Return Type:** Boolean - `true` if the IP address is within the range, `false` otherwise. Returns NULL or throws an error for invalid inputs. **Examples:** ```sql SELECT ipv6_in_range('2001:db8::1', '2001:db8::/32'); -- Output: true SELECT ipv6_in_range('2001:db8:1::', '2001:db8::/32'); -- Output: true SELECT ipv6_in_range('2001:db9::1', '2001:db8::/32'); -- Output: false SELECT ipv6_in_range('::1', '::1/128'); -- Output: true SELECT ipv6_in_range('fe80::1', 'fe80::/16'); -- Output: true ``` --- ## JSON Functions (Experimental) This page describes GreptimeDB functions for converting and extracting JSON values. :::warning The JSON feature is currently experimental and may change in future releases. ::: ## Conversion Conversion between JSON and other types. * `parse_json(string)` to parse a JSON string into a JSON value. Illegal JSON strings will return an error. * `json_to_string(json)` to convert a JSON value to a string. ```sql SELECT json_to_string(parse_json('{"a": 1, "b": 2}')); +----------------------------------------------------------+ | json_to_string(parse_json(Utf8("{\"a\": 1, \"b\": 2}"))) | +----------------------------------------------------------+ | {"a":1,"b":2} | +----------------------------------------------------------+ ``` ## Extraction Extracts values with specific types from JSON values through specific paths. * `json_get_bool(json, path)` to extract a boolean value from a JSON value by the path. * `json_get_int(json, path)` to extract an integer value from a JSON value by the path, while boolean values will be converted to integers. * `json_get_float(json, path)` to extract a float value from a JSON value by the path, while integer and boolean values will be converted to floats. * `json_get_string(json, path)` to extract a string value from a JSON value by the path. All valid JSON values will be converted to strings, including null values, objects and arrays. * `json_get_object(json, path)` to extract an object value from a JSON value by the path. Returns NULL if the path does not point to an object. * `json_get(json, path)` to extract a value as a string. Cast the function result to extract a scalar with another SQL type, for example `json_get(value, 'a')::INT`. The `path` argument to `json_get` must be a string literal. The return value is NULL when the path does not select a value or the selected value cannot be converted to the requested type. `json_to_string`, `json_get`, `json_get_string`, `json_get_int`, `json_get_float`, and `json_get_bool` also accept [JSON2](/user-guide/logs/json2.md) columns. `path` is a string that selects elements from a JSON value. The following path operators are supported: | Operator | Description | Examples | | ------------------------ | ------------------------------------------------------------ | ------------------ | | `$` | The root element | `$` | | `@` | The current element in the filter expression | `$.event?(@ == 1)` | | `.*` | Selecting all elements in an Object | `$.*` | | `.` | Selecting element that match the name in an Object | `$.event` | | `:` | Alias of `.` | `$:event` | | `[""]` | Alias of `.` | `$["event"]` | | `[*]` | Selecting all elements in an Array | `$[*]` | | `[, ..]` | Selecting 0-based `n-th` elements in an Array | `$[1, 2]` | | `[last - , ..]` | Selecting `n-th` element before the last element in an Array | `$[0, last - 1]` | | `[ to , ..]` | Selecting all elements of a range in an Array | `$[1 to last - 2]` | | `?()` | Selecting all elements that matched the filter expression | `$?(@.price < 10)` | If the path is invalid, the function will return a NULL value. ```sql SELECT json_get_int(parse_json('{"a": {"c": 3}, "b": 2}'), 'a.c'); +-----------------------------------------------------------------------+ | json_get_int(parse_json(Utf8("{"a": {"c": 3}, "b": 2}")),Utf8("a.c")) | +-----------------------------------------------------------------------+ | 3 | +-----------------------------------------------------------------------+ SELECT json_get(parse_json('{"a": 3}'), 'a')::INT; SELECT json_to_string(json_get_object(parse_json('{"a": {"b": {"c": {"d": 42}}}}'), 'a.b.c')); +---------------------------------------------------------------------------------------------------+ | json_to_string(json_get_object(parse_json(Utf8("{"a": {"b": {"c": {"d": 42}}}}")),Utf8("a.b.c"))) | +---------------------------------------------------------------------------------------------------+ | {"d":42} | +---------------------------------------------------------------------------------------------------+ ``` ## Validation Check the type of a JSON value. * `json_is_null(json)` to check whether a JSON value is a null value. * `json_is_bool(json)` to check whether a JSON value is a boolean value. * `json_is_int(json)` to check whether a JSON value is an integer value. * `json_is_float(json)` to check whether a JSON value is a float value. * `json_is_string(json)` to check whether a JSON value is a string value. * `json_is_object(json)` to check whether a JSON value is an object value. * `json_is_array(json)` to check whether a JSON value is an array value. ```sql SELECT json_is_array(parse_json('[1, 2, 3]')); +----------------------------------------------+ | json_is_array(parse_json(Utf8("[1, 2, 3]"))) | +----------------------------------------------+ | 1 | +----------------------------------------------+ SELECT json_is_object(parse_json('1')); +---------------------------------------+ | json_is_object(parse_json(Utf8("1"))) | +---------------------------------------+ | 0 | +---------------------------------------+ ``` * `json_path_exists(json, path)` to check whether a path exists in a JSON value. If the path is invalid, the function will return an error. If the path or the JSON value is `NULL`, the function will return a `NULL` value. ```sql SELECT json_path_exists(parse_json('{"a": 1, "b": 2}'), 'a'); +------------------------------------------------------------------+ | json_path_exists(parse_json(Utf8("{"a": 1, "b": 2}")),Utf8("a")) | +------------------------------------------------------------------+ | 1 | +------------------------------------------------------------------+ SELECT json_path_exists(parse_json('{"a": 1, "b": 2}'), 'c.d'); +--------------------------------------------------------------------+ | json_path_exists(parse_json(Utf8("{"a": 1, "b": 2}")),Utf8("c.d")) | +--------------------------------------------------------------------+ | 0 | +--------------------------------------------------------------------+ SELECT json_path_exists(parse_json('{"a": 1, "b": 2}'), NULL); +-------------------------------------------------------------+ | json_path_exists(parse_json(Utf8("{"a": 1, "b": 2}")),NULL) | +-------------------------------------------------------------+ | NULL | +-------------------------------------------------------------+ ``` * `json_path_match(json, path)` to check whether a JSON value matches the predicate in the specified JSON path expression. Only predicate expressions are supported. If the path is invalid or does not evaluate to a predicate, the function will return a `NULL` value. If the JSON value is `NULL`, the function will return a `NULL` value. ```sql SELECT json_path_match(parse_json('{"a": 1, "b": 2}'), '$.a == 1'); +------------------------------------------------------------------------+ | json_path_match(parse_json(Utf8("{"a": 1, "b": 2}")),Utf8("$.a == 1")) | +------------------------------------------------------------------------+ | 1 | +------------------------------------------------------------------------+ SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), '$.b[1 to last] >= 2'); +--------------------------------------------------------------------------------------+ | json_path_match(parse_json(Utf8("{"a":1,"b":[1,2,3]}")),Utf8("$.b[1 to last] >= 2")) | +--------------------------------------------------------------------------------------+ | 1 | +--------------------------------------------------------------------------------------+ SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), '$.b[0] > 1'); +-----------------------------------------------------------------------------+ | json_path_match(parse_json(Utf8("{"a":1,"b":[1,2,3]}")),Utf8("$.b[0] > 1")) | +-----------------------------------------------------------------------------+ | 0 | +-----------------------------------------------------------------------------+ ``` --- ## Functions Use this page as a quick index to GreptimeDB function references. ## Function References - [DataFusion Functions](./df-functions.md) - GreptimeDB Functions - [Core Functions (string, math, date/time, system, admin)](./greptimedb.md) - [Geospatial Functions](./geo.md) - [IP Functions](./ip.md) - [JSON Functions](./json.md) - [Vector Functions](./vector.md) - [Approximate Functions](./approximate.md) - [Anomaly Detection Functions](./anomaly.md) --- ## Vector Functions This page lists all supported vector-related functions in GreptimeDB. Vector functions are primarily used for handling vector operations, such as basic arithmetic, distance calculations, conversion functions, and more. ## Basic Operations ### `vec_scalar_add` Adds a scalar to a vector. Each element in the vector is added to the scalar, returning a new vector. Examples: ```sql SELECT vec_to_string(vec_scalar_add(2.0, parse_vec('[1.0, 2.0, 3.0]'))); ``` ```sql +------------------------------------------------------------------------------+ | vec_to_string(vec_scalar_add(Float64(2),parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +------------------------------------------------------------------------------+ | [3,4,5] | +------------------------------------------------------------------------------+ ``` ``` SELECT vec_to_string(vec_scalar_add(2.0, '[1.0, 2.0, 3.0]')); -- Implicitly convert string to vector ``` ```sql +-------------------------------------------------------------------+ | vec_to_string(vec_scalar_add(Float64(2),Utf8("[1.0, 2.0, 3.0]"))) | +-------------------------------------------------------------------+ | [3,4,5] | +-------------------------------------------------------------------+ ``` ``` SELECT vec_to_string(vec_scalar_add(-2.0, parse_vec('[1.0, 2.0, 3.0]'))); -- Subtraction ``` ```sql +-------------------------------------------------------------------------------+ | vec_to_string(vec_scalar_add(Float64(-2),parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +-------------------------------------------------------------------------------+ | [-1,0,1] | +-------------------------------------------------------------------------------+ ``` ### `vec_scalar_mul` Multiplies a vector by a scalar. Each element in the vector is multiplied by the scalar, returning a new vector. Examples: ```sql SELECT vec_to_string(vec_scalar_mul(2.0, parse_vec('[1.0, 2.0, 3.0]'))); ``` ```sql +------------------------------------------------------------------------------+ | vec_to_string(vec_scalar_mul(Float64(2),parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +------------------------------------------------------------------------------+ | [2,4,6] | +------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_scalar_mul(2.0, '[1.0, 2.0, 3.0]')); -- Implicitly convert string to vector ``` ```sql +------------------------------------------------------------------------------+ | vec_to_string(vec_scalar_mul(Float64(2),parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +------------------------------------------------------------------------------+ | [2,4,6] | +------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_scalar_mul(1.0/2.0, parse_vec('[1.0, 2.0, 3.0]'))); -- Division ``` ```sql +-------------------------------------------------------------------------------------------+ | vec_to_string(vec_scalar_mul(Float64(1) / Float64(2),parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +-------------------------------------------------------------------------------------------+ | [0.5,1,1.5] | +-------------------------------------------------------------------------------------------+ ``` ### `vec_add` Adds two vectors element-wise. Returns a new vector where each element is the sum of corresponding elements in the input vectors. Examples: ```sql SELECT vec_to_string(vec_add(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]'))); ``` ```sql +-----------------------------------------------------------------------------------------------+ | vec_to_string(vec_add(parse_vec(Utf8("[1.0, 2.0, 3.0]")),parse_vec(Utf8("[2.0, 1.0, 4.0]")))) | +-----------------------------------------------------------------------------------------------+ | [3,3,7] | +-----------------------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_add('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]')); -- Implicitly convert strings to vectors ``` ```sql +-------------------------------------------------------------------------+ | vec_to_string(vec_add(Utf8("[1.0, 2.0, 3.0]"),Utf8("[2.0, 1.0, 4.0]"))) | +-------------------------------------------------------------------------+ | [3,3,7] | +-------------------------------------------------------------------------+ ``` ### `vec_sub` Subtracts two vectors element-wise. Returns a new vector where each element is the difference of corresponding elements in the input vectors. Examples: ```sql SELECT vec_to_string(vec_sub(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]'))); ``` ```sql +-----------------------------------------------------------------------------------------------+ | vec_to_string(vec_sub(parse_vec(Utf8("[1.0, 2.0, 3.0]")),parse_vec(Utf8("[2.0, 1.0, 4.0]")))) | +-----------------------------------------------------------------------------------------------+ | [-1,1,-1] | +-----------------------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_sub('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]')); -- Implicitly convert strings to vectors ``` ```sql +-------------------------------------------------------------------------+ | vec_to_string(vec_sub(Utf8("[1.0, 2.0, 3.0]"),Utf8("[2.0, 1.0, 4.0]"))) | +-------------------------------------------------------------------------+ | [-1,1,-1] | +-------------------------------------------------------------------------+ ``` ### `vec_mul` Multiplies two vectors element-wise. Returns a new vector where each element is the product of corresponding elements in the input vectors. Examples: ```sql SELECT vec_to_string(vec_mul(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]'))); ``` ```sql +-----------------------------------------------------------------------------------------------+ | vec_to_string(vec_mul(parse_vec(Utf8("[1.0, 2.0, 3.0]")),parse_vec(Utf8("[2.0, 1.0, 4.0]")))) | +-----------------------------------------------------------------------------------------------+ | [2,2,12] | +-----------------------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_mul('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]')); -- Implicitly convert strings to vectors ``` ```sql +-------------------------------------------------------------------------+ | vec_to_string(vec_mul(Utf8("[1.0, 2.0, 3.0]"),Utf8("[2.0, 1.0, 4.0]"))) | +-------------------------------------------------------------------------+ | [2,2,12] | +-------------------------------------------------------------------------+ ``` ### `vec_div` Divides two vectors element-wise. Returns a new vector where each element is the quotient of corresponding elements in the input vectors. Examples: ```sql SELECT vec_to_string(vec_div(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]'))); ``` ```sql +-----------------------------------------------------------------------------------------------+ | vec_to_string(vec_div(parse_vec(Utf8("[1.0, 2.0, 3.0]")),parse_vec(Utf8("[2.0, 1.0, 4.0]")))) | +-----------------------------------------------------------------------------------------------+ | [0.5,2,0.75] | +-----------------------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_div('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]')); -- Implicitly convert strings to vectors ``` ```sql +-------------------------------------------------------------------------+ | vec_to_string(vec_div(Utf8("[1.0, 2.0, 3.0]"),Utf8("[2.0, 1.0, 4.0]"))) | +-------------------------------------------------------------------------+ | [0.5,2,0.75] | +-------------------------------------------------------------------------+ ``` ### `vec_elem_sum` Sums all elements of a vector, returning a scalar value. Examples: ```sql SELECT vec_elem_sum(parse_vec('[1.0, 2.0, 3.0]')); ``` ```sql +--------------------------------------------------+ | vec_elem_sum(parse_vec(Utf8("[1.0, 2.0, 3.0]"))) | +--------------------------------------------------+ | 6 | +--------------------------------------------------+ ``` ```sql SELECT vec_elem_sum('[1.0, 2.0, 3.0]'); -- Implicitly convert string to vector ``` ```sql +---------------------------------------+ | vec_elem_sum(Utf8("[1.0, 2.0, 3.0]")) | +---------------------------------------+ | 6 | +---------------------------------------+ ``` ### `vec_elem_product` Computes the product of all elements in a vector, returning a scalar value. Examples: ```sql SELECT vec_elem_product(parse_vec('[1.0, 2.0, 3.0]')); ``` ```sql +------------------------------------------------------+ | vec_elem_product(parse_vec(Utf8("[1.0, 2.0, 3.0]"))) | +------------------------------------------------------+ | 6 | +------------------------------------------------------+ ``` ```sql SELECT vec_elem_product('[1.0, 2.0, 3.0]'); -- Implicitly convert string to vector ``` ```sql +-------------------------------------------+ | vec_elem_product(Utf8("[1.0, 2.0, 3.0]")) | +-------------------------------------------+ | 6 | +-------------------------------------------+ ``` ### `vec_elem_avg` Returns the arithmetic mean of the elements in one vector. ```sql SELECT vec_elem_avg('[1.0, 2.0, 3.0]'); -- 2.0 ``` ### `vec_dim` Returns the number of elements in a vector as an unsigned integer. ```sql SELECT vec_dim('[7.0, 8.0, 9.0, 10.0]'); -- 4 ``` ### `vec_kth_elem` Returns the element at the zero-based integer index. A negative or out-of-range index is an error. ```sql SELECT vec_kth_elem('[2.0, 4.0, 6.0]', 1); -- 4.0 ``` ### `vec_subvector` Returns the elements in the zero-based half-open range `[start, end)`. The function reports an error unless `0 <= start <= end <= vec_dim(vector)`. ```sql SELECT vec_to_string(vec_subvector('[1.0, 2.0, 3.0, 4.0]', 1, 3)); -- [2,3] ``` ### `vec_norm` Normalizes a vector. Divides each element of the vector by the L2 norm of the vector, returning a new unit vector. Equivalent to `vec_scalar_mul(1.0 / sqrt(vec_elem_sum(vec_mul(vec, vec))), vec)`. Examples: ```sql SELECT vec_to_string(vec_norm(parse_vec('[1.0, 2.0, 3.0]'))); ``` ```sql +-------------------------------------------------------------+ | vec_to_string(vec_norm(parse_vec(Utf8("[1.0, 2.0, 3.0]")))) | +-------------------------------------------------------------+ | [0.26726124,0.5345225,0.8017837] | +-------------------------------------------------------------+ -- Equivalent to: -- SELECT vec_to_string(vec_scalar_mul(1.0 / sqrt(vec_elem_sum(vec_mul(vec, vec))), vec)) -- FROM (SELECT '[1.0, 2.0, 3.0]' AS vec); -- +--------------------------------------------------------------------------------------+ -- | vec_to_string(vec_scalar_mul(Float64(1) / sqrt(vec_elem_sum(vec_mul(vec,vec))),vec)) | -- +--------------------------------------------------------------------------------------+ -- | [0.26726124,0.5345225,0.8017837] | -- +--------------------------------------------------------------------------------------+ ``` ```sql SELECT vec_to_string(vec_norm('[1.0, 2.0, 3.0]')); -- Implicitly convert string to vector ``` ```sql +--------------------------------------------------+ | vec_to_string(vec_norm(Utf8("[1.0, 2.0, 3.0]"))) | +--------------------------------------------------+ | [0.26726124,0.5345225,0.8017837] | +--------------------------------------------------+ ``` ## Aggregate Functions ### `vec_sum` Sums all vectors in a vector column element-wise, returning a new vector. Examples: ```sql CREATE TABLE vectors ( ts TIMESTAMP TIME INDEX, vec_col VECTOR(3), ); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:01', '[1.0, 2.0, 3.0]'); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:02', '[2.0, 1.0, 4.0]'); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:03', '[3.0, 3.0, 3.0]'); SELECT vec_to_string(vec_sum(vec_col)) FROM vectors; ``` ```sql +-----------------------------------------+ | vec_to_string(vec_sum(vectors.vec_col)) | +-----------------------------------------+ | [6,6,10] | +-----------------------------------------+ ``` ### `vec_avg` Computes the element-wise average of all non-NULL vectors. All input vectors must have the same dimension. ```sql SELECT vec_to_string(vec_avg(vec_col)) FROM vectors; ``` ```sql +-----------------------------------------+ | vec_to_string(vec_avg(vectors.vec_col)) | +-----------------------------------------+ | [2,2,3.3333333] | +-----------------------------------------+ ``` ### `vec_product` Multiplies all vectors in a vector column element-wise, returning a new vector. Examples: ```sql CREATE TABLE vectors ( ts TIMESTAMP TIME INDEX, vec_col VECTOR(3), ); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:01', '[1.0, 2.0, 3.0]'); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:02', '[2.0, 1.0, 4.0]'); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:03', '[3.0, 3.0, 3.0]'); SELECT vec_to_string(vec_product(vec_col)) FROM vectors; ``` ```sql +---------------------------------------------+ | vec_to_string(vec_product(vectors.vec_col)) | +---------------------------------------------+ | [6,6,36] | +---------------------------------------------+ ``` ## Distance Calculations * `vec_l2sq_distance(vec1, vec2)`: Computes the squared L2 distance between two vectors. * `vec_cos_distance(vec1, vec2)`: Computes the cosine distance between two vectors. * `vec_dot_product(vec1, vec2)`: Computes the dot product of two vectors. These functions accept vector values as parameters. You can use the `parse_vec` function to convert a string into a vector value, such as `parse_vec('[1.0, 2.0, 3.0]')`. Also, vector strings (e.g., `[1.0, 2.0, 3.0]`) can be used directly and will be automatically converted. Regardless of the method used, the dimensionality of the vectors must remain consistent. --- ### `vec_l2sq_distance` Calculates the squared Euclidean distance (squared L2 distance) between two vectors. L2 distance is the straight-line distance between two points in geometric space. This function returns the squared value to improve computational efficiency. Example: ```sql SELECT vec_l2sq_distance(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]')); ``` Or ```sql SELECT vec_l2sq_distance('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]'); ``` Details: * Parameters are two vectors with consistent dimensions. * Output: A scalar value of type `Float32`. --- ### `vec_cos_distance` Calculates the cosine distance between two vectors. Cosine distance measures the cosine of the angle between two vectors and is used to quantify similarity. Example: ```sql SELECT vec_cos_distance(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]')); ``` Or ```sql SELECT vec_cos_distance('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]'); ``` Details: * Parameters are two vectors with consistent dimensions. * Output: A scalar value of type `Float32`. --- ### `vec_dot_product` Computes the dot product of two vectors. The dot product is the sum of the element-wise multiplications of two vectors. It is commonly used to measure similarity or for linear transformations in machine learning. Example: ```sql SELECT vec_dot_product(parse_vec('[1.0, 2.0, 3.0]'), parse_vec('[2.0, 1.0, 4.0]')); ``` Or ```sql SELECT vec_dot_product('[1.0, 2.0, 3.0]', '[2.0, 1.0, 4.0]'); ``` Details: * Parameters are two vectors with consistent dimensions. * Output: A scalar value of type `Float32`. ## Conversion Functions When dealing with vector data in the database, GreptimeDB provides convenient functions for converting between strings and vector values. ### `parse_vec` Converts a string to a vector value. The string must be enclosed in square brackets `[]` and contain elements of type `Float32`, separated by commas. Example: ```sql CREATE TABLE vectors ( ts TIMESTAMP, vec_col VECTOR(3) ); INSERT INTO vectors (ts, vec_col) VALUES ('2024-11-18 00:00:01', parse_vec('[1.0, 2.0, 3.0]')); ``` ### `vec_to_string` Converts a vector object to a string. The converted string format is `[, , ...]`. Example: ```sql SELECT vec_to_string(vec_col) FROM vectors; ``` --- ## events(Greptime-private) The `events` table stores events recorded while GreptimeDB runs. Standalone deployments record supported local DDL Procedure events. Distributed deployments with Metasrv can additionally record operational event types. Event recording is asynchronous and best-effort, so rows are not an acknowledgement that an operation succeeded. The table is created when the first event is recorded. If no event has been recorded, or event recording is disabled, querying it returns a table-not-found error. Configure recording in [Event recording](/user-guide/deployments-administration/configuration.md#event-recording). ```sql USE greptime_private; SELECT timestamp, type FROM events WHERE timestamp >= now() - INTERVAL '1' hour ORDER BY timestamp DESC LIMIT 20; ``` For column definitions, see [Event data model](/user-guide/deployments-administration/monitoring/events/event-data-model.md). For DDL Procedure query examples, see [DDL events](/user-guide/deployments-administration/monitoring/events/ddl-events.md). --- ## Greptime Private GreptimeDB stores some important internal information as system tables in the `greptime_private` database. Similar to the normal tables, the system tables will be persistently stored. You can obtain system configurations and statistical information through the system tables. ## Tables | Table Name | Description | | ----------------------------------- | --------------------------------------------------------------------------------------------- | | [`events`](./events.md) | Stores events recorded while GreptimeDB runs. | | [`slow_queries`](./slow_queries.md) | Contains GreptimeDB slow query information, including query statements, execution times, etc. | | [`pipelines`](./pipelines.md) | Contains GreptimeDB Pipeline information. | --- ## pipelines The `pipelines` table contains GreptimeDB Pipeline information. ```sql USE greptime_private; SELECT * FROM pipelines; ``` The output is as follows: ```sql +----------------+--------+--------------+----------------------------------------------------------------------------------------------------------------------------------+----------------------------+ | name | schema | content_type | pipeline | created_at | +----------------+--------+--------------+----------------------------------------------------------------------------------------------------------------------------------+----------------------------+ | nginx_pipeline | | yaml | transform: | 2025-07-03 07:23:15.227539 | - fields: - response_size type: int32 - fields: - timestamp type: time index: timestamp +----------------+--------+--------------+----------------------------------------------------------------------------------------------------------------------------------+----------------------------+ ``` - `name`: The name of the pipeline; - `schema`: Deprecated. If you create the pipeline after `v0.15`, this field should be an empty string. - `content_type`: The type of the pipeline; - `pipeline`: The content of the pipeline; - `created_at`: The creation time of the pipeline; For more details, please refer to the [Manage Pipelines](/user-guide/logs/manage-pipelines.md) documentation. --- ## slow_queries The `slow_queries` table records SQL and PromQL queries selected by the slow-query recorder. :::tip NOTE The `slow_queries` table requires slow query logging to be enabled. See [Slow Query](/user-guide/deployments-administration/monitoring/slow-query.md) for configuration details. ::: ```sql USE greptime_private; SELECT * FROM slow_queries ORDER BY timestamp DESC; ``` | Column | Type | Description | | --- | --- | --- | | `type` | `String` | Event type. Slow-query rows use `slow_query`. | | `payload` | `Json` | Structured diagnostic context attached to the event. It can be JSON `null`. | | `timestamp` | `TimestampNanosecond` | Time when the query event was recorded. | | `cost` | `UInt64` | Query duration in milliseconds. | | `threshold` | `UInt64` | Slow-query threshold in milliseconds. | | `query` | `String` | SQL statement, PromQL expression, or planned query text. | | `is_promql` | `Boolean` | Whether the row describes a PromQL query. | | `promql_range` | `UInt64` | PromQL query range in milliseconds; `0` for non-PromQL queries. | | `promql_step` | `UInt64` | PromQL step in milliseconds; `0` for non-PromQL queries. | | `promql_start` | `TimestampMillisecond` | PromQL range start; Unix epoch for non-PromQL queries. | | `promql_end` | `TimestampMillisecond` | PromQL range end; Unix epoch for non-PromQL queries. | | `schema_name` | `String` | Database schema in which the query ran. | See [Slow Query](/user-guide/deployments-administration/monitoring/slow-query.md) for configuration and retention behavior. --- ## GROUP BY The `GROUP BY` clause in SQL is used to group rows that have the same values in one or more columns. This clause is typically used with aggregate functions such as `COUNT`, `SUM`, `AVG`, etc., to generate summary reports. ## Syntax The basic syntax of the `GROUP BY` clause is as follows: ```sql SELECT column1, column2, ..., aggregate_function(column_name) FROM table_name GROUP BY column1, column2, ...; ``` The `GROUP BY` clause groups the result set based on the columns specified in the clause. The aggregate function is applied to each group of rows that have the same values in the specified columns. ## Examples Consider the following table named "system_metrics": ```sql +-------+-------+----------+-------------+-----------+---------------------+ | host | idc | cpu_util | memory_util | disk_util | ts | +-------+-------+----------+-------------+-----------+---------------------+ | host1 | idc_a | 11.8 | 10.3 | 10.3 | 2022-11-03 03:39:57 | | host1 | idc_b | 50 | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_c | 50.1 | 66.8 | 40.8 | 2022-11-03 03:39:57 | | host1 | idc_e | NULL | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host2 | idc_a | 80.1 | 70.3 | 90 | 2022-11-03 03:39:57 | +-------+-------+----------+-------------+-----------+---------------------+ ``` ### Group by Tags To get the avg memory_util for each idc, the following SQL query can be used: ```sql SELECT idc, AVG(memory_util) FROM system_metrics GROUP BY idc; ``` The result of the above query would be: ```sql +-------+---------------------------------+ | idc | AVG(system_metrics.memory_util) | +-------+---------------------------------+ | idc_b | 66.7 | | idc_c | 66.8 | | idc_e | 66.7 | | idc_a | 40.3 | +-------+---------------------------------+ ``` ### Group by Time Interval To get the avg memory_util for each day, the following SQL query can be used: ```sql SELECT date_trunc('day', ts) as dt, avg(memory_util) FROM system_metrics GROUP BY dt ``` Please refer to [date_trunc](./functions/df-functions.md#date_trunc) for more details. --- ## HAVING `WHERE` filters input rows before grouping. `HAVING` filters grouped or aggregated results after grouping. ## Examples Find days where the average CPU utilization exceeded 80%: ```sql SELECT date_trunc('day', ts) AS day, AVG(cpu_util) AS avg_cpu_util FROM system_metrics GROUP BY day HAVING avg_cpu_util > 80; ``` Find hours where the number of error logs is greater than 100: ```sql SELECT DATE_TRUNC('hour', log_time) AS hour, COUNT(*) AS error_count FROM application_logs WHERE log_level = 'ERROR' GROUP BY hour HAVING error_count > 100; ``` --- ## Alerts :::tip NOTE This feature is only available in the GreptimeDB Enterprise database. ::: The `ALERTS` table provides the information about all alert instances generated by triggers. ```sql DESC TABLE INFORMATION_SCHEMA.ALERTS; ``` ```sql +--------------+---------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------+---------------------+------+------+---------+---------------+ | trigger_id | UInt64 | | NO | | FIELD | | trigger_name | String | | NO | | FIELD | | labels | Json | | NO | | FIELD | | annotations | Json | | YES | | FIELD | | status | String | | NO | | FIELD | | active_at | TimestampNanosecond | | NO | | FIELD | | fired_at | TimestampNanosecond | | YES | | FIELD | | resolved_at | TimestampNanosecond | | YES | | FIELD | | last_sent_at | TimestampNanosecond | | YES | | FIELD | +--------------+---------------------+------+------+---------+---------------+ ``` The columns in table: * `trigger_id`: the id of the trigger that generated the alert instance. * `trigger_name`: the name of the trigger that generated the alert instance. * `labels`: the key-value pairs associated with the alert instance. And the label set uniquely identifies an alert instance. * `annotations`: the key-value pairs associated with the alert instance. * `status`: the current status of the alert instance. * `active_at`: the timestamp when the alert instance became active. * `fired_at`: the timestamp when the alert instance first entered the `Firing` state. * `resolved_at`: the timestamp when the alert instance was resolved. * `last_sent_at`: the timestamp when the last notification for the alert instance was sent. --- ## BUILD_INFO The `BUILD_INFO` table provides the system build info: ```sql USE INFORMATION_SCHEMA; SELECT * FROM BUILD_INFO; ``` The output is as follows: ```sql +------------+------------------------------------------+------------------+-----------+-------------+ | git_branch | git_commit | git_commit_short | git_clean | pkg_version | +------------+------------------------------------------+------------------+-----------+-------------+ | | c595a56ac89bef78b19a76aa60d8c6bcac7354a5 | c595a56a | true | 0.9.0 | +------------+------------------------------------------+------------------+-----------+-------------+ ``` The columns in the output: * `git_branch`: the build git branch name. * `git_commit`: the build commit revision. * `git_commit_short`: the short commit revision. * `git_clean`: `true` if the source working tree had no uncommitted changes at build time. * `pkg_version`: the GreptimeDB version. --- ## CHARACTER_SETS The `CHARACTER_SETS` provides the available character sets that GreptimeDB supports. ```sql USE INFORMATION_SCHEMA; SELECT * FROM CHARACTER_SETS; ``` The output is as follows: ```sql +--------------------+----------------------+---------------+--------+ | character_set_name | default_collate_name | description | maxlen | +--------------------+----------------------+---------------+--------+ | utf8 | utf8_bin | UTF-8 Unicode | 4 | +--------------------+----------------------+---------------+--------+ ``` The columns in the output: * `character_set_name`: the name of the character set. * `default_collate_name`: the default collation name of the character set. * `description`: the description of the character set. * `MAXLEN`: the maximum number of bytes required to store one character. --- ## CLUSTER_INFO The `CLUSTER_INFO` table provides the topology information of the cluster. ```sql USE INFORMATION_SCHEMA; DESC TABLE CLUSTER_INFO; ``` The output is as follows: ```sql +-----------------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------------+----------------------+------+------+---------+---------------+ | peer_id | Int64 | | NO | | FIELD | | peer_type | String | | NO | | FIELD | | peer_addr | String | | YES | | FIELD | | peer_hostname | String | | YES | | FIELD | | total_cpu_millicores | Int64 | | NO | | FIELD | | total_memory_bytes | Int64 | | NO | | FIELD | | cpu_usage_millicores | Int64 | | NO | | FIELD | | memory_usage_bytes | Int64 | | NO | | FIELD | | version | String | | NO | | FIELD | | git_commit | String | | NO | | FIELD | | start_time | TimestampMillisecond | | YES | | FIELD | | uptime | String | | YES | | FIELD | | active_time | String | | YES | | FIELD | | node_status | String | | YES | | FIELD | +-----------------------+----------------------+------+------+---------+---------------+ ``` The columns in table: * `peer_id`: the server id of the node. It's always `0` for standalone mode and `-1` for frontends because it doesn't make sense in such cases. * `peer_type`: the node type, `METASRV`,`FRONTEND`, or `DATANODE` for distributed clusters and `STANDALONE` for standalone deployments. * `peer_addr`: the GRPC server address of the node. It's always empty for standalone deployments. * `peer_hostname`: the hostname of the peer. * `total_cpu_millicores`: the total CPU millicores of the node. * `total_memory_bytes`: the total memory bytes of the node. * `cpu_usage_millicores`: the CPU usage millicores of the node. This only works in a containerized environment (cgroup v2). * `memory_usage_bytes`: the memory usage bytes of the node. This only works in a containerized environment (cgroup v2). * `version`: The build version of the node, such as `0.7.2` etc. * `git_commit`: The build git commit of the node. * `start_time`: The node start time. * `uptime`: The uptime of the node, in the format of duration string `24h 10m 59s 150ms`. * `active_time`: The duration string in the format of `24h 10m 59s 150ms` since the node's last active time(sending the heartbeats), it's always empty for standalone deployments. * `node_status`: the status info of the peer. Query the table: ```sql SELECT * FROM CLUSTER_INFO; ``` An example output in standalone deployments: ```sql +---------+------------+-----------+---------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+--------+-------------+-------------+ | peer_id | peer_type | peer_addr | peer_hostname | total_cpu_millicores | total_memory_bytes | cpu_usage_millicores | memory_usage_bytes | version | git_commit| start_time | uptime | active_time | node_status | +---------+------------+-----------+---------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+--------+-------------+-------------+ | 0 | STANDALONE | | | 16000 | 17179869184 | 0 | 0 | 0.7.2 | 86ab3d9 | 2024-04-30T06:40:02.074 | 18ms | | NULL | +---------+------------+-----------+---------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+--------+-------------+-------------+ ``` Another example is from a distributed cluster that has three Datanodes, one Frontend, and one Metasrv. ```sql +---------+-----------+----------------+-------------------------------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+----------+-------------+-------------------------------------------------------------------+ | peer_id | peer_type | peer_addr | peer_hostname | total_cpu_millicores | total_memory_bytes | cpu_usage_millicores | memory_usage_bytes | version | git_commit| start_time | uptime | active_time | node_status | +---------+-----------+----------------+-------------------------------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+----------+-------------+-------------------------------------------------------------------+ | 1 | DATANODE | 127.0.0.1:4101 | mycluster-datanode-0 | 1000 | 1073741824 | 1 | 34570240 | 0.7.2 | 86ab3d9 | 2024-04-30T06:40:04.791 | 4s 478ms | 1s 467ms | {"workloads":["hybrid"],"leader_regions":46,"follower_regions":0} | | 2 | DATANODE | 127.0.0.1:4102 | mycluster-datanode-1 | 1000 | 1073741824 | 1 | 38170624 | 0.7.2 | 86ab3d9 | 2024-04-30T06:40:06.098 | 3s 171ms | 162ms | {"workloads":["hybrid"],"leader_regions":46,"follower_regions":0} | | 3 | DATANODE | 127.0.0.1:4103 | mycluster-datanode-2 | 1000 | 1073741824 | 1 | 37085184 | 0.7.2 | 86ab3d9 | 2024-04-30T06:40:07.425 | 1s 844ms | 1s 839ms | {"workloads":["hybrid"],"leader_regions":46,"follower_regions":0} | | -1 | FRONTEND | 127.0.0.1:4001 | mycluster-frontend-6c5d4bcf78-m7jtx | 1000 | 1073741824 | 1 | 45465600 | 0.7.2 | 86ab3d9 | 2024-04-30T06:40:08.815 | 454ms | 47ms | NULL | | 0 | METASRV | 127.0.0.1:3002 | mycluster-meta-54bd44bd94-g8dzm | 1000 | 1073741824 | 0 | 28368896 | unknown | unknown | | | | {"is_leader":true} | +---------+-----------+----------------+-------------------------------------+----------------------+--------------------+----------------------+--------------------+---------+-----------+----------------------------+----------+-------------+-------------------------------------------------------------------+ ``` --- ## COLLATION_CHARACTER_SET_APPLICABILITY The `COLLATION_CHARACTER_SET_APPLICABILITY` table indicates what character set is applicable for what collation. ```sql USE INFORMATION_SCHEMA; SELECT * FROM COLLATION_CHARACTER_SET_APPLICABILITY; ``` The output is as follows: ```sql +----------------+--------------------+ | collation_name | character_set_name | +----------------+--------------------+ | utf8_bin | utf8 | +----------------+--------------------+ ``` The output has these columns: * `collation_name`: the collation name. * `character_set_name`: the name of the character set with which the collation is associated. --- ## COLLATIONS The `COLLATIONS` provides information about collations for each character set. ```sql USE INFORMATION_SCHEMA; SELECT * FROM COLLATIONS; ``` The output is as follows: ```sql +----------------+--------------------+------+------------+-------------+---------+ | collation_name | character_set_name | id | is_default | is_compiled | sortlen | +----------------+--------------------+------+------------+-------------+---------+ | utf8_bin | utf8 | 1 | Yes | Yes | 1 | +----------------+--------------------+------+------------+-------------+---------+ ``` The table has these columns: * `collation_name`: the collation name. * `character_set_name`: the name of the character set. * `id`: the collation ID. * `is_default`: Whether this collation is the default collation of the character set it belongs to. * `is_compiled`: Whether the character set is compiled into the system. * `sortlen`: the minimum amount of memory required to sort strings expressed in the character set. --- ## COLUMNS The `COLUMNS` provides detailed information about columns in tables. ```sql USE INFORMATION_SCHEMA; DESC COLUMNS; ``` The output is as follows: ```sql +--------------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------------+--------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | column_name | String | | NO | | FIELD | | ordinal_position | Int64 | | NO | | FIELD | | character_maximum_length | Int64 | | YES | | FIELD | | character_octet_length | Int64 | | YES | | FIELD | | numeric_precision | Int64 | | YES | | FIELD | | numeric_scale | Int64 | | YES | | FIELD | | datetime_precision | Int64 | | YES | | FIELD | | character_set_name | String | | YES | | FIELD | | collation_name | String | | YES | | FIELD | | column_key | String | | NO | | FIELD | | extra | String | | NO | | FIELD | | privileges | String | | NO | | FIELD | | generation_expression | String | | NO | | FIELD | | greptime_data_type | String | | NO | | FIELD | | data_type | String | | NO | | FIELD | | semantic_type | String | | NO | | FIELD | | column_default | String | | YES | | FIELD | | is_nullable | String | | NO | | FIELD | | column_type | String | | NO | | FIELD | | column_comment | String | | YES | | FIELD | | srs_id | Int64 | | YES | | FIELD | +--------------------------+--------+------+------+---------+---------------+ 24 rows in set (0.00 sec) ``` Create a table `public.t1` and query the information in the `COLUMNS` table: ```sql CREATE TABLE public.t1 (h STRING, v FLOAT64, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP() TIME INDEX, PRIMARY KEY(h)); SELECT * FROM COLUMNS WHERE table_schema='public' AND TABLE_NAME='t1'\G ``` The output is as follows: ```sql *************************** 1. row *************************** table_catalog: greptime table_schema: public table_name: t1 column_name: h ordinal_position: 1 character_maximum_length: 2147483647 character_octet_length: 2147483647 numeric_precision: NULL numeric_scale: NULL datetime_precision: NULL character_set_name: utf8 collation_name: utf8_bin column_key: PRI extra: privileges: select,insert generation_expression: greptime_data_type: String data_type: string semantic_type: TAG column_default: NULL is_nullable: YES column_type: string column_comment: NULL srs_id: NULL *************************** 2. row *************************** table_catalog: greptime table_schema: public table_name: t1 column_name: v ordinal_position: 2 character_maximum_length: NULL character_octet_length: NULL numeric_precision: 22 numeric_scale: NULL datetime_precision: NULL character_set_name: NULL collation_name: NULL column_key: extra: privileges: select,insert generation_expression: greptime_data_type: Float64 data_type: double semantic_type: FIELD column_default: NULL is_nullable: YES column_type: double column_comment: NULL srs_id: NULL *************************** 3. row *************************** table_catalog: greptime table_schema: public table_name: t1 column_name: ts ordinal_position: 3 character_maximum_length: NULL character_octet_length: NULL numeric_precision: NULL numeric_scale: NULL datetime_precision: 3 character_set_name: NULL collation_name: NULL column_key: TIME INDEX extra: privileges: select,insert generation_expression: greptime_data_type: TimestampMillisecond data_type: timestamp(3) semantic_type: TIMESTAMP column_default: current_timestamp() is_nullable: NO column_type: timestamp(3) column_comment: NULL srs_id: NULL 3 rows in set (0.03 sec) ``` The description of columns in the `COLUMNS` table is as follows: - `table_catalog`: The name of the catalog to which the table with the column belongs. The value is always `greptime` in OSS project. - `table_schema`: The name of the database in which the table with the column is located. - `table_name`: The name of the table with the column. - `column_name`: The name of the column. - `ordinal_position`: The position of the column in the table. - `character_maximum_length`: For string columns, the maximum length in characters. - `character_octet_length`: For string columns, the maximum length in bytes. - `numeric_precision`: The precision of the column for numeric data types. - `numeric_scale`: The scale of the column for numeric data types. - `datetime_precision`: The fractional seconds precision of the column for datetime data types. - `character_set_name`: The name of the character set of a string column. - `collation_name`: The name of the collation of a string column. - `column_key`: The key type of the column. It can be one of the following: `PRI`, `TIME INDEX`, or an empty string. - `extra`: Additional information about the column. - `privileges`: The privilege that the current user has on this column. - `generation_expression`: For generated columns, this value displays the expression used to calculate the column value. For non-generated columns, the value is empty. - `greptime_data_type`: The GreptimeDB [data type](/reference/sql/data-types.md) of the column. - `data_type`: The type of data in the column. - `semantic_type`: The type of the column. It can be one of the following: `TAG`, `FIELD`, or `TIMESTAMP`. - `column_default`: The default value of the column. If the explicit default value is `NULL`, or if the column definition does not include the `default` clause, this value is `NULL`. - `is_nullable`: Whether the column is nullable. If the column can store null values, this value is `YES`; otherwise, it is `NO`. - `column_type`: The data type of the column. It is the same as the `DATA_TYPE` column. - `column_comment`: Comments contained in the column definition. - `srs_id`: The ID of the spatial reference system (SRS) of the column. The corresponding `SHOW` statement is as follows: ```sql SHOW COLUMNS FROM t1 FROM public; ``` The output is as follows: ```sql +-------+--------------+------+------------+---------------------+-------+----------------------+ | Field | Type | Null | Key | Default | Extra | Greptime_type | +-------+--------------+------+------------+---------------------+-------+----------------------+ | h | string | Yes | PRI | NULL | | String | | ts | timestamp(3) | No | TIME INDEX | current_timestamp() | | TimestampMillisecond | | v | double | Yes | | NULL | | Float64 | +-------+--------------+------+------------+---------------------+-------+----------------------+ 3 rows in set (0.01 sec) ``` --- ## ENGINES The `ENGINES` table provides information about storage engines. This is particularly useful for checking whether a storage engine is supported, or to see what the default engine is. The `ENGINES` table has the following columns: * `engine`: the storage engine name. * `support`: the level of support for the storage engine: | Value | Meaning | | --- | --- | | `YES` | The engine is supported and is active | | `DEFAULT` | Like `YES`, plus this is the default engine | | `NO` | The engine is not supported | | `DISABLED` | The engine is supported but has been disabled | * `comment`: A brief description of the storage engine. * `transactions`: Whether the storage engine supports transactions. * `xa`: Whether the storage engine supports XA transactions. * `savepoints`: Whether the storage engine supports savepoints. For example: ```sql SELECT * from INFORMATION_SCHEMA.ENGINES\G ``` The output is as follows: ```sql *************************** 1. row *************************** engine: mito support: DEFAULT comment: Storage engine for time-series data transactions: NO xa: NO savepoints: NO *************************** 2. row *************************** engine: metric support: YES comment: Storage engine for observability scenarios, which is adept at handling a large number of small tables, making it particularly suitable for cloud-native monitoring transactions: NO xa: NO savepoints: NO ``` --- ## FLOW_STATISTICS The `FLOW_STATISTICS` table provides runtime statistics for flows. It lists every flow in the current catalog, including flows that have not reported statistics yet. ```sql DESC TABLE INFORMATION_SCHEMA.FLOW_STATISTICS; ``` ```sql +---------------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------------+----------------------+------+------+---------+---------------+ | flow_id | UInt32 | | NO | | FIELD | | flow_name | String | | NO | | FIELD | | start_time | TimestampMillisecond | | YES | | FIELD | | last_execution_time | TimestampMillisecond | | YES | | FIELD | | uptime_seconds | Int64 | | YES | | FIELD | | state_size | UInt64 | | YES | | FIELD | +---------------------+----------------------+------+------+---------+---------------+ ``` Fields in the `FLOW_STATISTICS` table are described as follows: - `flow_id`: The ID of the flow. - `flow_name`: The name of the flow. - `start_time`: Timestamp indicating when the Flownode started running the flow. - `last_execution_time`: Timestamp of the flow's most recent execution. - `uptime_seconds`: Seconds elapsed since `start_time`. - `state_size`: Size of the flow's in-memory state, in bytes. Statistics are reported by Flownodes through heartbeats. The last four columns are `NULL` for a flow that has no statistics reported yet, for example immediately after the flow is created or after a Flownode restart. In a cluster, `start_time` and `uptime_seconds` are always `NULL`: the Flownode state persisted for the cluster does not carry a flow's start time. Both columns are populated in standalone deployments. [`SHOW FLOW STATUS`](/reference/sql/show.md#show-flow-status) returns the same columns. --- ## FLOWS The `Flows` table provides the flow task information. ```sql DESC TABLE INFORMATION_SCHEMA.FLOWS; ``` ```sql Column | Type | Key | Null | Default | Semantic Type ------------------+--------+-----+------+---------+--------------- flow_name | String | | NO | | FIELD flow_id | UInt32 | | NO | | FIELD table_catalog | String | | NO | | FIELD flow_definition | String | | NO | | FIELD comment | String | | YES | | FIELD expire_after | Int64 | | YES | | FIELD source_table_ids | String | | YES | | FIELD sink_table_name | String | | NO | | FIELD flownode_ids | String | | YES | | FIELD options | String | | YES | | FIELD (10 rows) ``` The columns in table: * `flow_name`: the flow task's name. * `flow_id`: the flow task's id. * `table_catalog`: the catalog this flow belongs to, named as `table_catalog` to keep consistent with the `INFORMATION_SCHEMA` standard. * `flow_definition`: the flow task's definition. It's the SQL statement used to create the flow task. * `comment`: the comment of the flow task. * `expire_after`: the retention duration of the flow task, in seconds. * `source_table_ids`: the source table ids of the flow task. * `sink_table_name`: the sink table name of the flow task. * `flownode_ids`: the flownode ids used by the flow task. * `options`: extra options of the flow task. --- ## KEY_COLUMN_USAGE The `KEY_COLUMN_USAGE` table describes key constraints on columns, such as the time index and primary key constraints. Secondary index metadata is exposed in [`STATISTICS`](./statistics.md). ```sql USE INFORMATION_SCHEMA; DESC KEY_COLUMN_USAGE; ``` The output is as follows: ```sql +-------------------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-------------------------------+--------+------+------+---------+---------------+ | constraint_catalog | String | | NO | | FIELD | | constraint_schema | String | | NO | | FIELD | | constraint_name | String | | NO | | FIELD | | table_catalog | String | | NO | | FIELD | | real_table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | column_name | String | | NO | | FIELD | | ordinal_position | UInt32 | | NO | | FIELD | | position_in_unique_constraint | UInt32 | | YES | | FIELD | | referenced_table_schema | String | | YES | | FIELD | | referenced_table_name | String | | YES | | FIELD | | referenced_column_name | String | | YES | | FIELD | +-------------------------------+--------+------+------+---------+---------------+ ``` ```sql SELECT * FROM key_column_usage WHERE table_schema='public' and table_name='monitor'\G ``` ```sql *************************** 1. row *************************** constraint_catalog: def constraint_schema: public constraint_name: TIME INDEX table_catalog: def real_table_catalog: greptime table_schema: public table_name: monitor column_name: ts ordinal_position: 1 position_in_unique_constraint: NULL referenced_table_schema: NULL referenced_table_name: NULL referenced_column_name: NULL *************************** 2. row *************************** constraint_catalog: def constraint_schema: public constraint_name: PRIMARY table_catalog: def real_table_catalog: greptime table_schema: public table_name: monitor column_name: host ordinal_position: 1 position_in_unique_constraint: NULL referenced_table_schema: NULL referenced_table_name: NULL referenced_column_name: NULL 2 rows in set (0.02 sec) ``` The description of columns in the `KEY_COLUMN_USAGE` table is as follows: - `constraint_catalog`: The name of the catalog to which the constraint belongs. The value is always `def`. - `constraint_schema`: The name of the database to which the constraint belongs. - `constraint_name`: The name of the constraint. - `table_catalog`: The name of the catalog to which the table with the constraint belongs. The value is always `def`. - `real_table_catalog`: The real name of the catalog to which the table with the constraint belongs. The value is always `greptime`. - `table_schema`: The name of the database to which the table belongs. - `table_name`: The name of the table with the constraint. - `column_name`: The name of the column with the constraint. - `ordinal_position`: The position of the column in the constraint, rather than in the table. The position number starts from `1`. - `position_in_unique_constraint`: Reserved for foreign-key metadata. GreptimeDB does not support foreign keys, so this column is always `NULL`. - `referenced_table_schema`: Reserved for the referenced schema of a foreign key. The value is always `NULL`. - `referenced_table_name`: Reserved for the referenced table of a foreign key. The value is always `NULL`. - `referenced_column_name`: Reserved for the referenced column of a foreign key. The value is always `NULL`. --- ## INFORMATION_SCHEMA `INFORMATION_SCHEMA` provides access to metadata such as database and table names and column data types. GreptimeDB also provides custom `INFORMATION_SCHEMA` tables for system, cluster, and runtime metadata. Many `INFORMATION_SCHEMA` tables have a corresponding `SHOW` command. Unlike `SHOW`, `INFORMATION_SCHEMA` tables can be filtered, projected, and joined in SQL queries. The tables below distinguish implemented metadata tables from compatibility tables that return no rows or are not supported. ## Tables for MySQL compatibility | Table Name | Description | | --- | --- | | [`CHARACTER_SETS`](./character-sets.md) | provides information about available character sets. | | `CHECK_CONSTRAINTS`| Not implemented. Returns zero rows. | | [`COLLATIONS`](./collations.md) | Provides a list of collations that the server supports. | | [`COLLATION_CHARACTER_SET_APPLICABILITY`](./collation-character-set-applicability.md) | Explains which collations apply to which character sets. | | [`COLUMNS`](./columns.md) | Provides a list of columns for all tables. | | `COLUMN_PRIVILEGES` | Not implemented. Returns zero rows. | | `COLUMN_STATISTICS` | Not supported. | | [`ENGINES`](./engines.md) | Provides a list of supported storage engines. | | `EVENTS` | Not implemented. Returns zero rows. | | `FILES` | Not implemented. Returns zero rows. | | `GLOBAL_STATUS` | Not implemented. Returns zero rows. | | `GLOBAL_VARIABLES` | Not supported. | | [`KEY_COLUMN_USAGE`](./key-column-usage.md) | Describes the key constraints of the columns, such as the primary key, and time index constraint. | | `OPTIMIZER_TRACE` | Not implemented. Returns zero rows. | | `PARAMETERS` | Not implemented. Returns zero rows. | | [`PARTITIONS`](./partitions.md) | Provides a list of table partitions. | | `PLUGINS` | Not implemented. Returns zero rows. | | `PROCESSLIST` | Not implemented. Returns zero rows. | | `PROFILING` | Not implemented. Returns zero rows. | | `REFERENTIAL_CONSTRAINTS` | Not implemented. Returns zero rows. | | `ROUTINES` | Not implemented. Returns zero rows. | | [`SCHEMATA`](./schemata.md) | Provides similar information to `SHOW DATABASES`. | | `SCHEMA_PRIVILEGES` | Not implemented. Returns zero rows. | | `SESSION_STATUS` | Not implemented. Returns zero rows. | | `SESSION_VARIABLES` | Not supported. | | [`STATISTICS`](./statistics.md) | Provides index metadata in a MySQL-compatible shape. | | [`TABLES`](./tables.md) | Provides a list of tables that the current user has visibility of. Similar to `SHOW TABLES`. | | `TABLESPACES` | Not supported. | | `TABLE_PRIVILEGES` | Not implemented. Returns zero rows. | | `TRIGGERS` | Not supported. GreptimeDB Enterprise provides a differently shaped [`TRIGGERS`](./triggers.md) table. | | `USER_ATTRIBUTES` | Not supported. | | `USER_PRIVILEGES` | Not implemented. Returns zero rows. | | `VARIABLES_INFO` | Not supported. | | [`VIEWS`](./views.md)| Provides a list of views that the current user has visibility of. Similar to running `SHOW FULL TABLES WHERE table_type = 'VIEW'` | | [`TABLE_CONSTRAINTS`](./table-constraints.md) | Provides time-index and primary-key constraint metadata. | ## Tables that GreptimeDB provides | Table Name | Description | | --- | --- | | [`BUILD_INFO`](./build-info.md) | Provides the system build info. | | [`REGION_PEERS`](./region-peers.md) | Provides details about where regions are stored. | | [`REGION_INFO`](./region-info.md) | Provides Region runtime and manifest metadata, such as role and state, sequences, manifest version, Region options, SST format, and node ownership. | | [`REGION_STATISTICS`](./region-statistics.md) | Provides details about region statistics info, such as disk size, etc. | | [`CLUSTER_INFO`](./cluster-info.md)| Provides the topology information of the cluster.| | [`FLOWS`](./flows.md) | Provides the flow information.| | [`FLOW_STATISTICS`](./flow-statistics.md) | Provides runtime statistics for flows.| | [`PROCEDURE_INFO`](./procedure-info.md) | Procedure information.| | [`PROCESS_LIST`](./process-list.md) | Running queries information.| | [`RECYCLE_BIN`](./recycle-bin.md) | Provides soft-dropped tables that are still eligible for restore. | | [`TABLE_SEMANTICS`](./table-semantics.md) | Provides an experimental view over table semantic metadata. | | [`SSTS_INDEX_META`](./ssts-index-meta.md) | Provides SST index metadata including inverted indexes, fulltext indexes, and bloom filters.| | [`SSTS_MANIFEST`](./ssts-manifest.md) | Provides SST file information from the manifest including file paths, sizes, time ranges, row counts, and encoded primary key ranges.| | [`SSTS_STORAGE`](./ssts-storage.md) | Provides SST file information from the storage layer for verification and debugging.| | [`TRIGGERS`](./triggers.md) | Provides the trigger information. | | [`ALERTS`](./alerts.md) | Provides the alert information. | --- ## PARTITIONS The `PARTITIONS` table provides information about partitioned tables. ```sql USE INFORMATION_SCHEMA; DESC PARTITIONS; ``` The output is as follows: ```sql +-------------------------------+----------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-------------------------------+----------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | partition_name | String | | NO | | FIELD | | subpartition_name | String | | YES | | FIELD | | partition_ordinal_position | Int64 | | YES | | FIELD | | subpartition_ordinal_position | Int64 | | YES | | FIELD | | partition_method | String | | YES | | FIELD | | subpartition_method | String | | YES | | FIELD | | partition_expression | String | | YES | | FIELD | | subpartition_expression | String | | YES | | FIELD | | partition_description | String | | YES | | FIELD | | table_rows | Int64 | | YES | | FIELD | | avg_row_length | Int64 | | YES | | FIELD | | data_length | Int64 | | YES | | FIELD | | max_data_length | Int64 | | YES | | FIELD | | index_length | Int64 | | YES | | FIELD | | data_free | Int64 | | YES | | FIELD | | create_time | TimestampSecond | | YES | | FIELD | | update_time | TimestampSecond | | YES | | FIELD | | check_time | TimestampSecond | | YES | | FIELD | | checksum | Int64 | | YES | | FIELD | | partition_comment | String | | YES | | FIELD | | nodegroup | String | | YES | | FIELD | | tablespace_name | String | | YES | | FIELD | | greptime_partition_id | UInt64 | | YES | | FIELD | +-------------------------------+----------+------+------+---------+---------------+ 26 rows in set (0.01 sec) ``` Main columns: * `table_catalog`: The name of the catalog to which the table belongs, usually `greptime`. * `table_schema`: The name of the schema (database) to which the table belongs. * `table_name`: The name of the table containing the partition(region). * `partition_name`: The name of the partition(region). * `partition_ordinal_position`: All partitions are indexed in the same order as they are defined, with 1 being the number assigned to the first partition. * `partition_method`: This value is always `RANGE`, GreptimeDB only supports range partitioning. * `partition_expression`: The expression of this partition. * `create_time`: The time that the partition was created. * `greptime_partition_id`: GreptimeDB extended field, it's the Region Id. For example, create a partitioned table: ```sql CREATE TABLE public.test_p ( a INT PRIMARY KEY, b STRING, ts TIMESTAMP TIME INDEX, ) PARTITION ON COLUMNS (a) ( a < 10, a >= 10 AND a < 20, a >= 20 ); -- Query the partitions of the table SELECT * FROM PARTITIONS WHERE table_schema='public' AND table_name='test_p'\G ``` Outputs: ```sql *************************** 1. row *************************** table_catalog: greptime table_schema: public table_name: test_p partition_name: p0 subpartition_name: NULL partition_ordinal_position: 1 subpartition_ordinal_position: NULL partition_method: RANGE subpartition_method: NULL partition_expression: (a) VALUES LESS THAN (PartitionExpr { lhs: Column("a"), op: Lt, rhs: Value(Int32(10)) }) subpartition_expression: NULL partition_description: NULL table_rows: NULL avg_row_length: NULL data_length: NULL max_data_length: NULL index_length: NULL data_free: NULL create_time: 2024-04-01 10:49:49.468000 update_time: NULL check_time: NULL checksum: NULL partition_comment: NULL nodegroup: NULL tablespace_name: NULL greptime_partition_id: 4453881085952 *************************** 2. row *************************** table_catalog: greptime table_schema: public table_name: test_p partition_name: p1 subpartition_name: NULL partition_ordinal_position: 2 subpartition_ordinal_position: NULL partition_method: RANGE subpartition_method: NULL partition_expression: (a) VALUES LESS THAN (PartitionExpr { lhs: Column("a"), op: GtEq, rhs: Value(Int32(20)) }) subpartition_expression: NULL partition_description: NULL table_rows: NULL avg_row_length: NULL data_length: NULL max_data_length: NULL index_length: NULL data_free: NULL create_time: 2024-04-01 10:49:49.468000 update_time: NULL check_time: NULL checksum: NULL partition_comment: NULL nodegroup: NULL tablespace_name: NULL greptime_partition_id: 4453881085954 *************************** 3. row *************************** table_catalog: greptime table_schema: public table_name: test_p partition_name: p2 subpartition_name: NULL partition_ordinal_position: 3 subpartition_ordinal_position: NULL partition_method: RANGE subpartition_method: NULL partition_expression: (a) VALUES LESS THAN (PartitionExpr { lhs: Expr(PartitionExpr { lhs: Column("a"), op: Gt, rhs: Value(Int32(10)) }), op: And, rhs: Expr(PartitionExpr { lhs: Column("a"), op: Lt, rhs: Value(Int32(20)) }) }) subpartition_expression: NULL partition_description: NULL table_rows: NULL avg_row_length: NULL data_length: NULL max_data_length: NULL index_length: NULL data_free: NULL create_time: 2024-04-01 10:49:49.468000 update_time: NULL check_time: NULL checksum: NULL partition_comment: NULL nodegroup: NULL tablespace_name: NULL greptime_partition_id: 4453881085953 ``` --- ## PROCEDURE_INFO The `PROCEDURE_INFO` table provides detailed information about various procedures. ```sql DESC TABLE INFORMATION_SCHEMA.PROCEDURE_INFO; ``` ```sql +----------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +----------------+----------------------+------+------+---------+---------------+ | procedure_id | String | | NO | | FIELD | | procedure_type | String | | NO | | FIELD | | start_time | TimestampMillisecond | | YES | | FIELD | | end_time | TimestampMillisecond | | YES | | FIELD | | status | String | | NO | | FIELD | | lock_keys | String | | YES | | FIELD | +----------------+----------------------+------+------+---------+---------------+ ``` Fields in the `PROCEDURE_INFO` table are described as follows: - `procedure_id`: The ID of the Procedure. - `procedure_type`: The type of the Procedure. - `start_time`: Timestamp indicating when the procedure started. - `end_time`: Timestamp indicating when the procedure ended. - `status`: Current status of the procedure. - `lock_keys`: Keys locked by the procedure, if any. --- ## PROCESS_LIST The `PROCESS_LIST` table provides a view of all running queries in GreptimeDB cluster. :::tip NOTE It's intentionally to have a different table name than MySQL's `PROCESSLIST` because it has a very different set of columns. ::: ```sql USE INFORMATION_SCHEMA; DESC PROCESS_LIST; ``` The output is as follows: ```sql +-----------------+----------------------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------+----------------------+------+------+---------+---------------+ | id | String | | NO | | FIELD | | catalog | String | | NO | | FIELD | | schemas | String | | NO | | FIELD | | query | String | | NO | | FIELD | | client | String | | NO | | FIELD | | frontend | String | | NO | | FIELD | | start_timestamp | TimestampMillisecond | | NO | | FIELD | | elapsed_time | DurationMillisecond | | NO | | FIELD | +-----------------+----------------------+------+------+---------+---------------+ ``` Fields in the `PROCESS_LIST` table are described as follows: - `id`: The ID of query. - `catalog`: The catalog name of the query. - `schemas`: The schema name in which client issues the query. - `query`: The query statement. - `client`: Client information, including client address and channel. - `frontend`: On which frontend instance the query is running. - `start_timestamp`: The start timestamp of query. - `elapsed_time`: How long the query has been running. :::tip NOTE You can also use `SHOW [FULL] PROCESSLIST` statement as an alternative to querying from `INFORMATION_SCHEMA.PROCESS_LIST` table. ::: # Terminating a query When identified a running query from `PROCESS_LIST` table, you can terminate the query using `KILL ` statement, where the `` is the `id` field in `PROCESS_LIST` table. ```sql mysql> select * from process_list; +-----------------------+----------+--------------------+----------------------------+------------------------+---------------------+----------------------------+-----------------+ | id | catalog | schemas | query | client | frontend | start_timestamp | elapsed_time | +-----------------------+----------+--------------------+----------------------------+------------------------+---------------------+----------------------------+-----------------+ | 112.40.36.208/7 | greptime | public | SELECT * FROM some_very_large_table | mysql[127.0.0.1:34692] | 112.40.36.208:4001 | 2025-06-30 07:04:11.118000 | 00:00:12.002000 | +-----------------------+----------+--------------------+----------------------------+------------------------+---------------------+----------------------------+-----------------+ KILL '112.40.36.208/7'; Query OK, 1 row affected (0.00 sec) ``` --- ## RECYCLE_BIN :::tip NOTE This feature is only available in the GreptimeDB Enterprise database. ::: The `RECYCLE_BIN` table lists soft-dropped tables that are still eligible for restore. ```sql USE INFORMATION_SCHEMA; DESC RECYCLE_BIN; ``` The output is as follows: ```sql +-----------------------+----------------------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------------+----------------------+-----+------+---------+---------------+ | object_id | UInt64 | | NO | | FIELD | | object_type | String | | NO | | FIELD | | original_object_name | String | | NO | | FIELD | | original_catalog_name | String | | NO | | FIELD | | original_schema_name | String | | NO | | FIELD | | dropped_at | TimestampMillisecond | | YES | | FIELD | | dropped_by | String | | YES | | FIELD | | retention_expires_at | TimestampMillisecond | | YES | | FIELD | | purge_status | String | | NO | | FIELD | | restorable | Boolean | | NO | | FIELD | | restored_at | TimestampMillisecond | | YES | | FIELD | | restored_by | String | | YES | | FIELD | +-----------------------+----------------------+-----+------+---------+---------------+ ``` Query dropped tables: ```sql SELECT object_id, object_type, original_catalog_name, original_schema_name, original_object_name, dropped_at, retention_expires_at, purge_status, restorable FROM information_schema.recycle_bin WHERE original_schema_name = 'public'; ``` The description of columns in the `RECYCLE_BIN` table is as follows: - `object_id`: The ID of the dropped object. For tables, this is the original table ID. - `object_type`: The type of the dropped object. The current value is `TABLE`. - `original_object_name`: The original table name. - `original_catalog_name`: The original catalog name. - `original_schema_name`: The original database name. - `dropped_at`: The timestamp when the table was soft-dropped. It can be `NULL` for legacy tombstones without lifecycle markers. - `dropped_by`: The user that dropped the table. This field is reserved and is currently `NULL`. - `retention_expires_at`: The fixed timestamp when the table becomes eligible for automatic purge. It can be `NULL` for legacy tombstones without lifecycle markers. - `purge_status`: The purge status of the dropped object. Rows visible in the recycle bin use `ACTIVE`; tables already being purged are hidden. - `restorable`: Whether the dropped object is eligible for `UNDROP TABLE`. Rows visible in the recycle bin are restorable. - `restored_at`: The timestamp when the object was restored. This field is reserved and is currently `NULL` because restored objects no longer appear in the recycle bin. - `restored_by`: The user that restored the object. This field is reserved and is currently `NULL`. `information_schema.tables` only lists live tables. Use `information_schema.recycle_bin` to find soft-dropped tables and then restore them with `UNDROP TABLE` or permanently remove them with `ADMIN purge_table()`. --- ## REGION_INFO The `REGION_INFO` table provides runtime and manifest metadata for Regions. Use it to inspect a Region's role and state, writable status, sequence numbers, manifest version, Region options, SST format, and the Datanode that reports the Region. ```sql USE INFORMATION_SCHEMA; DESC REGION_INFO; ``` The output is as follows: ```sql +------------------------+---------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------------+---------+-----+------+---------+---------------+ | region_id | UInt64 | | NO | | FIELD | | table_id | UInt32 | | NO | | FIELD | | region_number | UInt32 | | NO | | FIELD | | region_group | UInt8 | | NO | | FIELD | | region_sequence | UInt32 | | NO | | FIELD | | state | String | | NO | | FIELD | | role | String | | NO | | FIELD | | writable | Boolean | | NO | | FIELD | | committed_sequence | UInt64 | | NO | | FIELD | | flushed_sequence | UInt64 | | YES | | FIELD | | manifest_version | UInt64 | | NO | | FIELD | | compaction_time_window | String | | YES | | FIELD | | region_options | String | | NO | | FIELD | | sst_format | String | | NO | | FIELD | | node_id | UInt64 | | YES | | FIELD | +------------------------+---------+-----+------+---------+---------------+ ``` Fields in the `REGION_INFO` table are described as follows: - `region_id`: The ID of the Region. - `table_id`: The ID of the table that the Region belongs to. - `region_number`: The Region number inside the table. - `region_group`: The Region group encoded in the Region ID. - `region_sequence`: The Region sequence inside the group. - `state`: The full runtime role and state of the Region. Possible values include `Follower`, `Leader(Writable)`, `Leader(Staging)`, `Leader(EnteringStaging)`, `Leader(Altering)`, `Leader(Dropping)`, `Leader(Truncating)`, `Leader(Editing)`, and `Leader(Downgrading)`. - `role`: The coarse Region role, `Leader` or `Follower`. - `writable`: Whether the Region accepts writes. - `committed_sequence`: The committed sequence of the Region. - `flushed_sequence`: The latest sequence persisted into SSTs. The value is `NULL` if no sequence has been flushed. - `manifest_version`: The manifest version of the Region. - `compaction_time_window`: The human-readable compaction time window of the Region. - `region_options`: The Region options encoded as JSON. - `sst_format`: The SST format used by the Region, such as `primary_key` or `flat`. - `node_id`: The ID of the Datanode that reports the row. The value can be `NULL` when the row is not reported by a Datanode. Query a table's Region runtime and manifest information as follows: ```sql SELECT i.region_id, i.state, i.role, i.writable, i.committed_sequence, i.flushed_sequence, i.manifest_version, i.compaction_time_window, i.region_options, i.sst_format, i.node_id FROM REGION_INFO i WHERE i.region_id IN ( SELECT region_id FROM REGION_PEERS WHERE table_name = 'system_metrics' ) ORDER BY i.region_id; ``` Query the maximum manifest version difference between leaders and followers as follows: ```sql SELECT MAX( CAST(l.manifest_version AS INT64) - CAST(f.manifest_version AS INT64) ) AS max_manifest_version_diff FROM REGION_INFO l JOIN REGION_INFO f ON l.region_id = f.region_id WHERE l.role = 'Leader' AND f.role = 'Follower'; ``` To find the Region and Datanode pair with the largest manifest version difference, use the following query: ```sql SELECT l.region_id, l.node_id AS leader_node_id, f.node_id AS follower_node_id, l.manifest_version AS leader_manifest_version, f.manifest_version AS follower_manifest_version, CAST(l.manifest_version AS INT64) - CAST(f.manifest_version AS INT64) AS manifest_version_diff FROM REGION_INFO l JOIN REGION_INFO f ON l.region_id = f.region_id WHERE l.role = 'Leader' AND f.role = 'Follower' ORDER BY manifest_version_diff DESC LIMIT 1; ``` --- ## REGION_PEERS The `REGION_PEERS` table shows detailed information of a single Region node in GreptimeDB, such as whether it is a learner or leader. ```sql USE INFORMATION_SCHEMA; DESC REGION_PEERS; ``` The output is as follows: ```sql +---------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------+--------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | region_id | UInt64 | | NO | | FIELD | | peer_id | UInt64 | | YES | | FIELD | | peer_addr | String | | YES | | FIELD | | is_leader | String | | YES | | FIELD | | status | String | | YES | | FIELD | | down_seconds | Int64 | | YES | | FIELD | +---------------+--------+------+------+---------+---------------+ 9 rows in set (0.00 sec) ``` Fields in the `REGION_PEERS` table are described as follows: - `table_catalog`: The catalog of the table. - `table_schema`: The schema of the table. - `table_name`: The name of the table. - `region_id`: The ID of the Region. - `peer_id`: The ID of the Region peer. - `peer_addr`: The address of the peer. - `is_leader`: Whether the peer is the leader. - `status`: The status of the peer, `ALIVE` or `DOWNGRADED`. - `ALIVE`: The peer is online. - `DOWNGRADED`: The Region peer is unavailable (e.g., crashed, network disconnected), or the Region peer was planned to migrate to another peer. - `down_seconds`: The duration of being offline, in seconds. --- ## REGION_STATISTICS The `REGION_STATISTICS` table provides detailed information about a region's statistics, including the total number of rows, disk size, and more. These statistics are approximate values. ```sql USE INFORMATION_SCHEMA; DESC REGION_STATISTICS; ``` The output is as follows: ```sql +--------------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------------+--------+------+------+---------+---------------+ | region_id | UInt64 | | NO | | FIELD | | table_id | UInt32 | | NO | | FIELD | | region_number | UInt32 | | NO | | FIELD | | region_rows | UInt64 | | YES | | FIELD | | written_bytes_since_open | UInt64 | | YES | | FIELD | | query_cpu_time_millis | UInt64 | | YES | | FIELD | | query_scanned_bytes | UInt64 | | YES | | FIELD | | disk_size | UInt64 | | YES | | FIELD | | memtable_size | UInt64 | | YES | | FIELD | | manifest_size | UInt64 | | YES | | FIELD | | sst_size | UInt64 | | YES | | FIELD | | sst_num | UInt64 | | YES | | FIELD | | index_size | UInt64 | | YES | | FIELD | | engine | String | | YES | | FIELD | | region_role | String | | YES | | FIELD | +--------------------------+--------+------+------+---------+---------------+ ``` Fields in the `REGION_STATISTICS` table are described as follows: - `region_id`: The ID of the Region. - `table_id`: The ID of the table. - `region_number`: The number of the region in the table. - `region_rows`: The number of rows in the region. It includes rows in SST files owned by this region and rows in memtables. Rows from SST files referenced from other regions are not counted. - `written_bytes_since_open`: The number of bytes written to the region since the region was opened. - `query_cpu_time_millis`: The cumulative query CPU time of the region since it was opened, in milliseconds. - `query_scanned_bytes`: The cumulative number of bytes scanned by queries since the region was opened. - `disk_size`: The total size of data files in the region, including data, index and metadata etc. - `memtable_size`: The region's total size of memtables. - `manifest_size`: The region's total size of manifest files. - `sst_size`: The region's total size of SST files owned by this region. SST files referenced from other regions are not counted. - `sst_num`: The region's total number of SST files owned by this region. SST files referenced from other regions are not counted. - `index_size`: The region's total size of index files owned by this region. SST index files referenced from other regions are not counted. - `engine`: The engine type of the region, `mito` or `metric`. - `region_role`: The region's role, `Leader` or `Follower`. Retrieve a table's region statistics information as follows: ```sql SELECT r.* FROM REGION_STATISTICS r LEFT JOIN TABLES t on r.table_id = t.table_id \ WHERE t.table_name = 'system_metrics'; ``` Output: ```sql +---------------+----------+---------------+-------------+--------------------------+------------------------+---------------------+-----------+---------------+---------------+----------+---------+------------+--------+-------------+ | region_id | table_id | region_number | region_rows | written_bytes_since_open | query_cpu_time_millis | query_scanned_bytes | disk_size | memtable_size | manifest_size | sst_size | sst_num | index_size | engine | region_role | +---------------+----------+---------------+-------------+--------------------------+------------------------+---------------------+-----------+---------------+---------------+----------+---------+------------+--------+-------------+ | 4398046511104 | 1024 | 0 | 8 | 0 | 0 | 0 | 4922 | 0 | 1338 | 3249 | 1 | 335 | mito | Leader | +---------------+----------+---------------+-------------+--------------------------+------------------------+---------------------+-----------+---------------+---------------+----------+---------+------------+--------+-------------+ ``` --- ## SCHEMATA The `SCHEMATA` table provides information about databases. The table data is equivalent to the result of the `SHOW DATABASES` statement. ```sql USE INFORMATION_SCHEMA; DESC SCHEMATA; ``` The output is as follows: ```sql +----------------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +----------------------------+--------+------+------+---------+---------------+ | catalog_name | String | | NO | | FIELD | | schema_name | String | | NO | | FIELD | | default_character_set_name | String | | NO | | FIELD | | default_collation_name | String | | NO | | FIELD | | sql_path | String | | YES | | FIELD | | options | String | | YES | | FIELD | +----------------------------+--------+------+------+---------+---------------+ ``` ```sql SELECT * FROM SCHEMATA; ``` ```sql +--------------+--------------------+----------------------------+------------------------+----------+-------------+ | catalog_name | schema_name | default_character_set_name | default_collation_name | sql_path | options | +--------------+--------------------+----------------------------+------------------------+----------+-------------+ | greptime | greptime_private | utf8 | utf8_bin | NULL | | | greptime | information_schema | utf8 | utf8_bin | NULL | | | greptime | public | utf8 | utf8_bin | NULL | | | greptime | test | utf8 | utf8_bin | NULL | ttl='7days' | +--------------+--------------------+----------------------------+------------------------+----------+-------------+ ``` Fields in the `SCHEMATA` table are described as follows: - `catalog_name`: The catalog to which the database belongs. - `schema_name`: The name of the database. - `default_character_set_name`: The default character set of the database. - `default_collation_name`: The default collation of the database. - `sql_path`: The value of this item is always `NULL`. - `options`: Extending column in GreptimeDB. The database options. --- ## SSTS_INDEX_META The `SSTS_INDEX_META` table provides access to SST (Sorted String Table) index metadata collected from the manifest. This table surfaces information about Puffin index metadata, including inverted indexes, fulltext indexes, and bloom filters. ```sql USE INFORMATION_SCHEMA; DESC SSTS_INDEX_META; ``` The output is as follows: ```sql +-----------------+--------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------+--------+-----+------+---------+---------------+ | table_dir | String | | NO | | FIELD | | index_file_path | String | | NO | | FIELD | | region_id | UInt64 | | NO | | FIELD | | table_id | UInt32 | | NO | | FIELD | | region_number | UInt32 | | NO | | FIELD | | region_group | UInt8 | | NO | | FIELD | | region_sequence | UInt32 | | NO | | FIELD | | file_id | String | | NO | | FIELD | | index_file_size | UInt64 | | YES | | FIELD | | index_type | String | | NO | | FIELD | | target_type | String | | NO | | FIELD | | target_key | String | | NO | | FIELD | | target_json | String | | NO | | FIELD | | blob_size | UInt64 | | NO | | FIELD | | meta_json | String | | YES | | FIELD | | node_id | UInt64 | | YES | | FIELD | +-----------------+--------+-----+------+---------+---------------+ ``` Fields in the `SSTS_INDEX_META` table are described as follows: - `table_dir`: The directory path of the table. - `index_file_path`: The full path to the Puffin index file. - `region_id`: The ID of the region. - `table_id`: The ID of the table. - `region_number`: The region number within the table. - `region_group`: The group identifier for the region. - `region_sequence`: The sequence number of the region. - `file_id`: The unique identifier of the index file (UUID). - `index_file_size`: The size of the index file in bytes. - `index_type`: The type of index. Possible values include: - `inverted`: Inverted index for fast term lookups - `fulltext_bloom`: Combined fulltext and bloom filter index - `bloom_filter`: Bloom filter for fast membership tests - `target_type`: The type of target being indexed. Typically `column` for column-based indexes. - `target_key`: The key identifying the target (e.g., column ID). - `target_json`: JSON representation of the target configuration, such as `{"column":0}`. - `blob_size`: The size of the blob data in bytes. - `meta_json`: JSON metadata containing index-specific information such as: - For inverted indexes: FST size, bitmap type, segment row count, etc. - For bloom filters: bloom filter size, row count, segment count - For fulltext indexes: analyzer type, case sensitivity settings - `node_id`: The ID of the datanode where the index is located. ## Examples Query all index metadata: ```sql SELECT * FROM INFORMATION_SCHEMA.SSTS_INDEX_META; ``` Query index metadata for a specific table by joining with the `TABLES` table: ```sql SELECT s.* FROM INFORMATION_SCHEMA.SSTS_INDEX_META s JOIN INFORMATION_SCHEMA.TABLES t ON s.table_id = t.table_id WHERE t.table_name = 'my_table'; ``` Query only inverted index metadata: ```sql SELECT table_dir, index_file_path, index_type, target_json, meta_json FROM INFORMATION_SCHEMA.SSTS_INDEX_META WHERE index_type = 'inverted'; ``` Query index metadata grouped by index type: ```sql SELECT index_type, COUNT(*) as count, SUM(index_file_size) as total_size FROM INFORMATION_SCHEMA.SSTS_INDEX_META GROUP BY index_type; ``` Output example: ```sql mysql> SELECT * FROM INFORMATION_SCHEMA.SSTS_INDEX_META LIMIT 1\G; *************************** 1. row *************************** table_dir: data/greptime/public/1814/ index_file_path: data/greptime/public/1814/1814_0000000000/data/index/aba4af59-1247-4bfb-a20b-69242cdd9374.puffin region_id: 7791070674944 table_id: 1814 region_number: 0 region_group: 0 region_sequence: 0 file_id: aba4af59-1247-4bfb-a20b-69242cdd9374 index_file_size: 838 index_type: bloom_filter target_type: column target_key: 2147483652 target_json: {"column":2147483652} blob_size: 688 meta_json: {"bloom":{"bloom_filter_size":640,"row_count":2242,"rows_per_segment":1024,"segment_count":3}} node_id: 0 1 row in set (0.02 sec) ``` --- ## SSTS_MANIFEST The `SSTS_MANIFEST` table provides access to SST (Sorted String Table) file information collected from the manifest. This table surfaces detailed information about each SST file, including file paths, sizes, levels, time ranges, row counts, and encoded primary key ranges. ```sql USE INFORMATION_SCHEMA; DESC SSTS_MANIFEST; ``` The output is as follows: ```sql +------------------+---------------------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------+---------------------+-----+------+---------+---------------+ | table_dir | String | | NO | | FIELD | | region_id | UInt64 | | NO | | FIELD | | table_id | UInt32 | | NO | | FIELD | | region_number | UInt32 | | NO | | FIELD | | region_group | UInt8 | | NO | | FIELD | | region_sequence | UInt32 | | NO | | FIELD | | file_id | String | | NO | | FIELD | | level | UInt8 | | NO | | FIELD | | file_path | String | | NO | | FIELD | | file_size | UInt64 | | NO | | FIELD | | index_file_path | String | | YES | | FIELD | | index_file_size | UInt64 | | YES | | FIELD | | num_rows | UInt64 | | NO | | FIELD | | num_row_groups | UInt64 | | NO | | FIELD | | min_ts | TimestampNanosecond | | YES | | FIELD | | max_ts | TimestampNanosecond | | YES | | FIELD | | sequence | UInt64 | | YES | | FIELD | | origin_region_id | UInt64 | | NO | | FIELD | | node_id | UInt64 | | YES | | FIELD | | visible | Boolean | | NO | | FIELD | | primary_key_min | Binary | | YES | | FIELD | | primary_key_max | Binary | | YES | | FIELD | +------------------+---------------------+-----+------+---------+---------------+ ``` Fields in the `SSTS_MANIFEST` table are described as follows: - `table_dir`: The directory path of the table. - `region_id`: The ID of the region that refers to the file. - `table_id`: The ID of the table. - `region_number`: The region number within the table. - `region_group`: The group identifier for the region. - `region_sequence`: The sequence number of the region. - `file_id`: The unique identifier of the SST file (UUID). - `level`: The SST level in the LSM tree (0 for uncompacted, 1 for compacted). - `file_path`: The full path to the SST file in object storage. - `file_size`: The size of the SST file in bytes. - `index_file_path`: The full path to the index file in object storage (if exists). - `index_file_size`: The size of the index file in bytes (if exists). - `num_rows`: The number of rows in the SST file. - `num_row_groups`: The number of row groups in the SST file. - `min_ts`: The minimum timestamp in the SST file. - `max_ts`: The maximum timestamp in the SST file. - `sequence`: The sequence number associated with this file. - `origin_region_id`: The ID of the region that created the file. - `node_id`: The ID of the datanode where the file is located. - `visible`: Whether this file is visible in the current version. - `primary_key_min`: The minimum encoded primary key in the SST file. - `primary_key_max`: The maximum encoded primary key in the SST file. ## Examples Query all SST files in the manifest: ```sql SELECT * FROM INFORMATION_SCHEMA.SSTS_MANIFEST; ``` Query SST files for a specific table by joining with the `TABLES` table: ```sql SELECT s.* FROM INFORMATION_SCHEMA.SSTS_MANIFEST s JOIN INFORMATION_SCHEMA.TABLES t ON s.table_id = t.table_id WHERE t.table_name = 'my_table'; ``` Query only compacted SST files (level 1): ```sql SELECT file_path, file_size, num_rows, level FROM INFORMATION_SCHEMA.SSTS_MANIFEST WHERE level = 1; ``` Query SST files with their time ranges: ```sql SELECT table_id, file_path, num_rows, min_ts, max_ts FROM INFORMATION_SCHEMA.SSTS_MANIFEST ORDER BY table_id, min_ts; ``` Calculate total SST file size per table: ```sql SELECT table_id, COUNT(*) as sst_count, SUM(file_size) as total_size FROM INFORMATION_SCHEMA.SSTS_MANIFEST GROUP BY table_id; ``` Output example: ```sql mysql> SELECT * FROM INFORMATION_SCHEMA.SSTS_MANIFEST LIMIT 1\G; *************************** 1. row *************************** table_dir: data/greptime/public/1024/ region_id: 4398046511104 table_id: 1024 region_number: 0 region_group: 0 region_sequence: 0 file_id: 01234567-89ab-cdef-0123-456789abcdef level: 0 file_path: data/greptime/public/1024/4398046511104_0/01234567-89ab-cdef-0123-456789abcdef.parquet file_size: 1234 index_file_path: data/greptime/public/1024/4398046511104_0/index/01234567-89ab-cdef-0123-456789abcdef.puffin index_file_size: 256 num_rows: 100 num_row_groups: 1 min_ts: 2025-01-01 00:00:00.000000000 max_ts: 2025-01-01 00:01:00.000000000 sequence: 1 origin_region_id: 4398046511104 node_id: 0 visible: true primary_key_min: 01800001f4 primary_key_max: 01800001f4 1 row in set (0.02 sec) ``` --- ## SSTS_STORAGE The `SSTS_STORAGE` table provides access to SST (Sorted String Table) file information listed directly from the storage layer. This table shows raw file metadata from object storage, which may include files that are not yet reflected in the manifest or files that have been orphaned. ```sql USE INFORMATION_SCHEMA; DESC SSTS_STORAGE; ``` The output is as follows: ```sql +------------------+----------------------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------+----------------------+-----+------+---------+---------------+ | file_path | String | | NO | | FIELD | | file_size | UInt64 | | YES | | FIELD | | last_modified_ms | TimestampMillisecond | | YES | | FIELD | | node_id | UInt64 | | YES | | FIELD | +------------------+----------------------+-----+------+---------+---------------+ ``` Fields in the `SSTS_STORAGE` table are described as follows: - `file_path`: The full path to the file in object storage. - `file_size`: The size of the file in bytes (if available from storage). - `last_modified_ms`: The last modified time in milliseconds (if available from storage). - `node_id`: The ID of the datanode where the file is located. ## Use Cases The `SSTS_STORAGE` table is useful for: - **Storage verification**: Compare files in storage against the manifest to detect orphaned files or inconsistencies. - **Storage debugging**: Identify files that exist in storage but may not be properly tracked in the manifest. - **Cleanup operations**: Find and remove orphaned SST files that are no longer referenced. - **Storage auditing**: Get a complete view of all SST files in the storage layer. ## Examples Query all SST files in storage: ```sql SELECT * FROM INFORMATION_SCHEMA.SSTS_STORAGE; ``` Find files in storage that are not in the manifest (potential orphaned files): ```sql SELECT s.file_path, s.file_size, s.last_modified_ms FROM INFORMATION_SCHEMA.SSTS_STORAGE s LEFT JOIN INFORMATION_SCHEMA.SSTS_MANIFEST m ON s.file_path = m.file_path WHERE m.file_path IS NULL; ``` Find the largest SST files in storage: ```sql SELECT file_path, file_size FROM INFORMATION_SCHEMA.SSTS_STORAGE WHERE file_size IS NOT NULL ORDER BY file_size DESC LIMIT 10; ``` Calculate total storage usage by SST files: ```sql SELECT COUNT(*) as file_count, SUM(file_size) as total_size FROM INFORMATION_SCHEMA.SSTS_STORAGE WHERE file_size IS NOT NULL; ``` Output example: ```sql mysql> SELECT * FROM INFORMATION_SCHEMA.SSTS_STORAGE LIMIT 1\G; *************************** 1. row *************************** file_path: data/greptime/public/1024/4398046511104_0/01234567-89ab-cdef-0123-456789abcdef.parquet file_size: 1234 last_modified_ms: 2025-01-01 00:00:00.000 node_id: 0 1 row in set (0.02 sec) ``` ## Differences from SSTS_MANIFEST | Aspect | SSTS_MANIFEST | SSTS_STORAGE | |--------|---------------|--------------| | **Data Source** | Manifest metadata | Storage layer directly | | **Information** | Detailed SST metadata (rows, time ranges, etc.) | Basic file metadata only | | **File Coverage** | Only files tracked in manifest | All files in storage | | **Use Case** | Query SST metadata for analysis | Verify storage, find orphaned files | | **Performance** | Fast (reads from manifest) | Slower (scans storage) | --- ## STATISTICS The `STATISTICS` table provides index metadata in a MySQL-compatible shape. It powers `SHOW INDEX` and returns one row per indexed column for primary keys, time indexes, inverted indexes, fulltext indexes, and skipping indexes. ```sql USE INFORMATION_SCHEMA; DESC STATISTICS; ``` The output is as follows: ```sql +---------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +---------------------+--------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | non_unique | Int64 | | NO | | FIELD | | index_schema | String | | NO | | FIELD | | index_name | String | | NO | | FIELD | | seq_in_index | Int64 | | NO | | FIELD | | column_name | String | | NO | | FIELD | | collation | String | | YES | | FIELD | | cardinality | Int64 | | YES | | FIELD | | sub_part | Int64 | | YES | | FIELD | | packed | String | | YES | | FIELD | | nullable | String | | NO | | FIELD | | index_type | String | | NO | | FIELD | | comment | String | | NO | | FIELD | | index_comment | String | | NO | | FIELD | | is_visible | String | | NO | | FIELD | | expression | String | | YES | | FIELD | | greptime_index_type | String | | YES | | FIELD | +---------------------+--------+------+------+---------+---------------+ ``` Create tables with different index types and query the metadata: ```sql CREATE TABLE IF NOT EXISTS system_metrics ( host STRING, idc STRING FULLTEXT INDEX INVERTED INDEX, cpu_util DOUBLE, memory_util DOUBLE, disk_util DOUBLE, desc1 STRING, desc2 STRING FULLTEXT INDEX, desc3 STRING FULLTEXT INDEX, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(host, idc), TIME INDEX(ts) ); CREATE TABLE IF NOT EXISTS test ( a STRING, b STRING SKIPPING INDEX, c DOUBLE, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(a, b), TIME INDEX(ts) ); SELECT table_schema, table_name, non_unique, index_name, seq_in_index, column_name, index_type, greptime_index_type FROM information_schema.statistics WHERE table_schema = 'public' AND table_name IN ('system_metrics', 'test') ORDER BY table_name, index_name, seq_in_index, column_name; ``` The output is as follows: ```sql +--------------+----------------+------------+----------------------+--------------+-------------+------------+---------------------+ | table_schema | table_name | non_unique | index_name | seq_in_index | column_name | index_type | greptime_index_type | +--------------+----------------+------------+----------------------+--------------+-------------+------------+---------------------+ | public | system_metrics | 1 | FULLTEXT_INDEX_desc2 | 1 | desc2 | FULLTEXT | fulltext_bloom | | public | system_metrics | 1 | FULLTEXT_INDEX_desc3 | 1 | desc3 | FULLTEXT | fulltext_bloom | | public | system_metrics | 1 | FULLTEXT_INDEX_idc | 1 | idc | FULLTEXT | fulltext_bloom | | public | system_metrics | 1 | INVERTED_INDEX_idc | 1 | idc | INVERTED | inverted | | public | system_metrics | 0 | PRIMARY | 1 | host | PRIMARY | dense | | public | system_metrics | 0 | PRIMARY | 2 | idc | PRIMARY | dense | | public | system_metrics | 1 | TIME INDEX | 1 | ts | TIME | | | public | test | 0 | PRIMARY | 1 | a | PRIMARY | dense | | public | test | 0 | PRIMARY | 2 | b | PRIMARY | dense | | public | test | 1 | SKIPPING_INDEX_b | 1 | b | SKIPPING | bloom_filter | | public | test | 1 | TIME INDEX | 1 | ts | TIME | | +--------------+----------------+------------+----------------------+--------------+-------------+------------+---------------------+ ``` The following statements are equivalent: ```sql SELECT table_name, non_unique, index_name, seq_in_index, column_name FROM information_schema.statistics WHERE table_schema = '' AND table_name = ''; SHOW INDEX FROM IN ; ``` The description of columns in the `STATISTICS` table is as follows: - `table_catalog`: The catalog of the table. The value is always `def`. - `table_schema`: The database of the table. - `table_name`: The name of the table. - `non_unique`: Whether the index can contain duplicate values. `0` means unique, `1` means non-unique. - `index_schema`: The database that contains the index metadata. It is the same as `table_schema`. - `index_name`: The index name. `PRIMARY` and `TIME INDEX` keep their names, while each secondary index is named `_INDEX_`, such as `INVERTED_INDEX_idc`, `FULLTEXT_INDEX_desc2`, or `SKIPPING_INDEX_b`. - `seq_in_index`: The position of the column inside the index, starting from `1`. Secondary indexes are single-column and always `1`; only a multi-column primary key produces `1`, `2`, and so on. - `column_name`: The indexed column name. - `collation`: The column sort direction in the index. The value is currently always `A`. - `cardinality`: The estimated number of unique values in the index. Currently `NULL`. - `sub_part`: The indexed prefix length. Currently `NULL`. - `packed`: How the key is packed. Currently `NULL`. - `nullable`: Whether the indexed column can be `NULL`. The value is `YES` or an empty string. - `index_type`: The index category, one of `PRIMARY`, `TIME`, `INVERTED`, `FULLTEXT`, or `SKIPPING`. - `comment`: Additional index metadata. Currently an empty string. - `index_comment`: User-facing index comments. Currently an empty string. - `is_visible`: Whether the index is visible. The value is currently always `YES`. - `expression`: The indexed expression for functional indexes. Currently `NULL`. - `greptime_index_type`: The GreptimeDB internal index type identifier: `dense` or `sparse` for primary keys, `inverted`, `fulltext_bloom` or `fulltext_tantivy`, and `bloom_filter` for skipping indexes. It is empty for the time index. --- ## TABLE_CONSTRAINTS The `TABLE_CONSTRAINTS` table describes which tables have constraints. ```sql DESC INFORMATION_SCHEMA.table_constraints; ``` ```sql +--------------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +--------------------+--------+------+------+---------+---------------+ | constraint_catalog | String | | NO | | FIELD | | constraint_schema | String | | NO | | FIELD | | constraint_name | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | constraint_type | String | | NO | | FIELD | | enforced | String | | NO | | FIELD | +--------------------+--------+------+------+---------+---------------+ ``` The columns in the table: * `CONSTRAINT_CATALOG`: The name of the catalog to which the constraint belongs. This value is always `def`. * `CONSTRAINT_SCHEMA`: The name of the database to which the constraint belongs. * `CONSTRAINT_NAME`: The name of the constraint, `TIME INDEX` or `PRIMARY`. * `TABLE_SCHEMA`: The name of the schema that contains the table. * `TABLE_NAME`: The name of the table. * `CONSTRAINT_TYPE`: The type of the constraint. The value can be `TIME INDEX` or `PRIMARY KEY`. These key constraints are also visible in [`STATISTICS`](./statistics.md), which backs `SHOW INDEX`. * `ENFORCED`: Whether the constraint is enforced. GreptimeDB reports `YES` for time-index and primary-key constraints. ```sql select * from INFORMATION_SCHEMA.table_constraints WHERE table_name = 'monitor'\G; ``` The output: ```sql *************************** 1. row *************************** constraint_catalog: def constraint_schema: public constraint_name: TIME INDEX table_schema: public table_name: monitor constraint_type: TIME INDEX enforced: YES *************************** 2. row *************************** constraint_catalog: def constraint_schema: public constraint_name: PRIMARY table_schema: public table_name: monitor constraint_type: PRIMARY KEY enforced: YES --- ## TABLE_SEMANTICS (Experimental) :::warning The `TABLE_SEMANTICS` view is currently experimental and may change in future releases. ::: The `TABLE_SEMANTICS` view provides a queryable interface over GreptimeDB's [table semantic layer](/user-guide/concepts/semantic-layer.md). It returns one row for each table that carries at least one `greptime.semantic.*` table option. Tables without any semantic option do not appear in this view. The semantic options describe what observability concept a table stands for, so a consumer (an LLM agent, an alert/dashboard builder, an ETL job) can align a table with its meaning without guessing from column names. They are set in two ways: - **Automatically on ingestion.** The OTLP and Prometheus remote write paths stamp the relevant keys when they auto-create a table. - **Manually on DDL.** You can set them yourself in `CREATE TABLE ... WITH (...)`. Only whitelisted keys with a valid value are accepted; an unknown key or an out-of-domain value is rejected. ```sql CREATE TABLE my_metrics ( ts TIMESTAMP TIME INDEX, val DOUBLE ) WITH ( 'greptime.semantic.signal_type' = 'metric', 'greptime.semantic.source' = 'opentelemetry', 'greptime.semantic.metric.type' = 'counter' ); ``` `signal_type`, `source`, `pipeline`, and `metadata_quality` are promoted to dedicated columns. The remaining semantic keys are folded into the `semantic_options` JSON string, with the `greptime.semantic.` prefix removed from each key. Because the promoted fields are regular columns, you can filter on them directly. ```sql USE INFORMATION_SCHEMA; DESC TABLE_SEMANTICS; ``` The output is as follows: ```sql +------------------+--------+-----+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------+--------+-----+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | table_id | UInt32 | | NO | | FIELD | | signal_type | String | | YES | | FIELD | | source | String | | YES | | FIELD | | pipeline | String | | YES | | FIELD | | metadata_quality | String | | YES | | FIELD | | semantic_options | String | | YES | | FIELD | +------------------+--------+-----+------+---------+---------------+ ``` ## Columns - `table_catalog`: The catalog name of the table. - `table_schema`: The schema name of the table. - `table_name`: The table name. - `table_id`: The internal table ID. - `signal_type`: The telemetry signal the table represents. One of: - `metric` — metrics. - `trace` — traces (spans). - `log` — logs. - `event` — discrete events. Reserved: it is a valid value you can set manually, but no ingestion path stamps it automatically yet. Today the ingestion paths only stamp `metric`, `log`, and `trace`. - `unknown` — could not be determined when the option was stamped. - `source`: The ingestion ecosystem that wrote the data. One of `opentelemetry`, `prometheus`, `influxdb`, `opentsdb`, `elasticsearch`, `loki`, `custom`, `mixed`, or `unknown`. `mixed` means one table received data from more than one source; `unknown` means it could not be determined. - `pipeline`: A free-form identifier of the internal ingestion data model. Today the OTLP trace path stamps `greptime_trace_v1`; other values can be set manually. It is the signal-agnostic successor to the `table_data_model` option. - `metadata_quality`: How the metric type metadata was obtained — that is, how much you can trust the `metric.type` value. One of: - `declared` — the ingestion protocol stated the instrument type explicitly (for example OTLP metrics carry the type). Trustworthy. - `inferred` — the type was guessed from the metric name (for example a Prometheus `_total` suffix is read as a counter). May be wrong for non-conventional names. - `unknown` — could not be determined. - `semantic_options`: A JSON string holding the remaining semantic options after the promoted keys are removed, with the `greptime.semantic.` prefix stripped from each key. The keys are sorted, so the string is stable across queries. See [Semantic options keys](#semantic-options-keys) below. ## Semantic options keys The following keys can appear inside `semantic_options`. They are signal-specific, which is why they are kept in the JSON tail rather than promoted to columns. - `metric.type`: The instrument kind. One of `counter`, `gauge`, `histogram`, `summary`, `updown_counter`, `gauge_histogram`, `info`, `stateset`, `mixed`, or `unknown`. `mixed` means one table holds more than one instrument kind. - `metric.unit`: The unit in [UCUM](https://ucum.org/) notation, for example `s`, `By`, or `{request}`. The row encoders discard the unit, so it is unrecoverable from the data once ingested. - `metric.temporality`: The aggregation temporality (OTLP only). One of `cumulative`, `delta`, `mixed`, or `unknown`. It is invisible in the metric name, so it is unrecoverable from the table alone. - `metric.original_name`: The pre-translation OpenTelemetry metric name, recorded when the table name was converted to Prometheus style. It is the key a consumer uses to look the metric up in the OpenTelemetry semantic conventions. - `trace.conventions`: The semantic-conventions version the rows conform to, typically an OpenTelemetry schema URL such as `https://opentelemetry.io/schemas/1.27.0`, or `unknown` / `mixed`. `unknown` and `mixed` are shared sentinels: `unknown` means the value could not be determined when the option was stamped, and `mixed` means a single-valued key saw conflicting values over the table's lifetime. ## Examples Query semantic metadata as follows: ```sql SELECT table_schema, table_name, signal_type, source, pipeline, metadata_quality, semantic_options FROM information_schema.table_semantics ORDER BY table_name; ``` Example output: ```sql +--------------+----------------+-------------+---------------+--------------------+------------------+-----------------------------------------------------------------+ | table_schema | table_name | signal_type | source | pipeline | metadata_quality | semantic_options | +--------------+----------------+-------------+---------------+--------------------+------------------+-----------------------------------------------------------------+ | public | metrics_tagged | metric | opentelemetry | | declared | {"metric.type":"counter","metric.unit":"By"} | | public | traces_tagged | trace | opentelemetry | greptime_trace_v1 | | {"trace.conventions":"https://opentelemetry.io/schemas/1.27.0"} | +--------------+----------------+-------------+---------------+--------------------+------------------+-----------------------------------------------------------------+ ``` Filter on promoted columns directly: ```sql SELECT table_name, signal_type FROM information_schema.table_semantics WHERE signal_type = 'metric' ORDER BY table_name; ``` ```sql +----------------+-------------+ | table_name | signal_type | +----------------+-------------+ | metrics_tagged | metric | +----------------+-------------+ ``` --- ## TABLES The `TABLES` table provides information about tables in databases: ```sql USE INFORMATION_SCHEMA; DESC TABLES; ``` The output is as follows: ```sql +------------------+----------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +------------------+----------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | table_type | String | | NO | | FIELD | | table_id | UInt32 | | YES | | FIELD | | data_length | UInt64 | | YES | | FIELD | | max_data_length | UInt64 | | YES | | FIELD | | index_length | UInt64 | | YES | | FIELD | | max_index_length | UInt64 | | YES | | FIELD | | avg_row_length | UInt64 | | YES | | FIELD | | engine | String | | YES | | FIELD | | version | UInt64 | | YES | | FIELD | | row_format | String | | YES | | FIELD | | table_rows | UInt64 | | YES | | FIELD | | data_free | UInt64 | | YES | | FIELD | | auto_increment | UInt64 | | YES | | FIELD | | create_time | TimestampSecond | | YES | | FIELD | | update_time | TimestampSecond | | YES | | FIELD | | check_time | TimestampSecond | | YES | | FIELD | | table_collation | String | | YES | | FIELD | | checksum | UInt64 | | YES | | FIELD | | create_options | String | | YES | | FIELD | | table_comment | String | | YES | | FIELD | | temporary | String | | YES | | FIELD | +------------------+----------+------+------+---------+---------------+ ``` ```sql SELECT * FROM tables WHERE table_schema='public' AND table_name='monitor'\G ``` ```sql *************************** 1. row *************************** table_catalog: greptime table_schema: public table_name: monitor table_type: BASE TABLE table_id: 1054 data_length: 0 max_data_length: 0 index_length: 0 max_index_length: 0 avg_row_length: 0 engine: mito version: 11 row_format: Fixed table_rows: 0 data_free: 0 auto_increment: 0 create_time: 2024-07-24 22:06:18.085000 update_time: NULL check_time: NULL table_collation: NULL checksum: 0 create_options: table_comment: NULL temporary: N 1 row in set (0.01 sec) ``` The following statements are equivalent: ```sql SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = '' [AND table_name LIKE 'monitor'] SHOW TABLES FROM db_name [LIKE 'monitor'] ``` The description of columns in the `TABLES` table is as follows: - `table_catalog`: The catalog to which the table belongs. The value is always `greptime`. - `table_schema`: The database to which the table belongs. - `table_name`: The name of the table. - `table_type`: The type of the table. - `BASE TABLE` for a table. - `TEMPORARY` for a temporary result set. - `VIEW` for a view. - `table_id`: The ID of the table. - `data_length`: The table size, representing the total length of the table's SST files in bytes. This is an approximate value. - `index_length`: The size of all indexes of the table, representing the total length of the table's index files in bytes. This is an approximate value. - `table_rows`: The table's total row number, which is an approximate value. - `avg_row_length`: The average row length in bytes, which is an approximate value. - `engine`: The storage engine of the table used. - `version`: Version. The value is `11` by default. - `create_time`: The table created timestamp. - `table_comment`: The table comment. - Other columns such as `auto_increment`, `row_format` etc. are not supported, just for compatibility with MySQL. GreptimeDB may support some of them in the future. --- ## Triggers :::tip NOTE This feature is only available in the GreptimeDB Enterprise database. ::: The `TRIGGERS` table provides metadata information about all triggers. ```sql DESC TABLE INFORMATION_SCHEMA.TRIGGERS; ``` ```sql +-----------------+--------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +-----------------+--------+------+------+---------+---------------+ | trigger_name | String | | NO | | FIELD | | trigger_id | UInt64 | | NO | | FIELD | | raw_sql | String | | NO | | FIELD | | interval | UInt64 | | NO | | FIELD | | labels | Json | | YES | | FIELD | | annotations | Json | | YES | | FIELD | | for | UInt64 | | YES | | FIELD | | keep_firing_for | UInt64 | | YES | | FIELD | | channels | Json | | YES | | FIELD | | flownode_id | UInt64 | | YES | | FIELD | +-----------------+--------+------+------+---------+---------------+ ``` The columns in table: * `trigger_name`: the name of the trigger. * `trigger_id`: the id of the trigger. * `raw_sql`: the SQL query executed periodically by the trigger. * `interval`: the execution interval of the trigger, in seconds. * `labels`: static key-value pairs defined through the `LABELS` clause. * `annotations`: static key-value pairs defined through the `ANNOTATIONS` clause. * `for`: how long an alert must remain active before it fires, in seconds. * `keep_firing_for`: how long an alert instance should remain in the `Firing` state after it first enters that state, in seconds. * `channels`: the notification channels used by the trigger. * `flownode_id`: the id of the flownode responsible for executing the trigger. --- ## VIEWS The `VIEWS` table provides a list of views that the current user has visibility of. ```sql DESC TABLE INFORMATION_SCHEMA.VIEWS; ``` ```sql +----------------------+---------+------+------+---------+---------------+ | Column | Type | Key | Null | Default | Semantic Type | +----------------------+---------+------+------+---------+---------------+ | table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | view_definition | String | | NO | | FIELD | | check_option | String | | YES | | FIELD | | is_updatable | String | | YES | | FIELD | | definer | String | | YES | | FIELD | | security_type | String | | YES | | FIELD | | character_set_client | String | | YES | | FIELD | | collation_connection | String | | YES | | FIELD | +----------------------+---------+------+------+---------+---------------+ ``` The columns in table: * `table_catalog`: The name of the catalog to which the view belongs. * `table_schema`: The name of the database to which the view belongs. * `table_name`: The view name. * `view_definition`: The definition of view, which is made by the `SELECT` statement when the view is created. * `check_option`: Doesn't support, is always `NULL`. * `is_updatable`: Whether `UPDATE/INSERT/DELETE` is applicable to the view, always `NO`. * `definer`: Reserved for the account that defines the view. The value is always `NULL`. * `security_type`: Doesn't support, is always `NULL`. * `character_set_client`: The value of the `character_set_client` session variable when the view is created, is always `utf8`. * `collation_connection`: The value of the `collation_connection` session variable when the view is created, is always `utf8_bin`. --- ## INSERT The `INSERT` statement is used to insert one or more records into a table in the GreptimeDB. ## `INSERT INTO` Statement ### Syntax The syntax for the INSERT INTO statement is as follows: ```sql INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...); ``` In the above syntax, `table_name` is the name of the table into which you want to insert data, and `column1`, `column2`, `column3`, etc. are the names of the columns in the table. You can specify the columns in the `INSERT INTO` statement if you want to insert data into specific columns. If you do not specify the columns, the values will be inserted into all the columns in the table. The `VALUES` keyword is followed by a list of values that correspond to the columns in the `INSERT INTO` statement. The values must be in the same order as the columns. The following types of values are supported: - The `DEFAULT` keyword specifies that the default value for that column should be inserted. This is useful when you do not want to explicitly specify values for some columns when inserting records. It allows the column's default value, as defined in the table schema, to be used instead. If no default value is defined for the column, the database's default value will be used (often NULL). - Use hexadecimal literal to insert a literal binary. - use the `{Value}::{Type}` syntax to cast the special numeric values `Infinity`, `-Infinity`, and `NaN` into the desired numeric type. ### Examples #### Insert Data Here is an example of an `INSERT INTO` statement that inserts a record into a table named `system_metrics`: ```sql INSERT INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_b", 50.0, 66.7, 40.6, 1667446797462); ``` This statement inserts a record with the host "host1", idc "idc_b", cpu_util 50.0, memory_util 66.7, disk_util 40.6, ts 1667446797462 into the `system_metrics` table. You can also use the `INSERT INTO` statement to insert multiple records into a table at the same time. Here is an example of an `INSERT INTO` statement that inserts multiple records into the `system_metrics` table: ```sql INSERT INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_a", 11.8, 10.3, 10.3, 1667446797460), ("host2", "idc_a", 80.1, 70.3, 90.0, 1667446797461), ("host1", "idc_c", 50.1, 66.8, 40.8, 1667446797463); ``` This statement inserts three records into the `system_metrics` table with the specified values for each column. #### Insert Data with Default Values The following sql statement use `DEFAULT` keyword to insert the default value for the `cpu_util` column: ```sql INSERT INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_a", DEFAULT, 10.3, 10.3, 1667446797460); ``` #### Insert Binary Data When we want to insert binary data which their column datatypes are `blob` or `bytea`: ```sql CREATE TABLE test(b BLOB, ts TIMESTAMP TIME INDEX); ``` Recommend using the prepared statement, JDBC for example: ```java PreparedStatement pstmt = conn.prepareStatement("insert into test values(?,?)"); pstmt.setBytes(1, "hello".getBytes()); pstmt.setInt(2, 1687867163); pstmt.addBatch(); ...... pstmt.executeBatch(); ``` If we want to insert a literal binary, we can insert a hexadecimal literal: ```sql INSERT INTO test VALUES(X'9fad5e9eefdfb449', 1687867163); -- or -- INSERT INTO test VALUES(0x9fad5e9eefdfb449, 1687867163); ``` #### Insert Special Numeric Values Use `{Value}::{Type}` to cast `Infinity`, `-Infinity`, and `NaN` into the desired numeric type: ```sql INSERT INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_a", 66.6, 'NaN'::double, 'Infinity'::double, 1667446797460); ``` ## `INSERT INTO SELECT` Statement The statement copies data from one table and inserts it into another table. ### Syntax The syntax for the `INSERT INTO SELECT` statement is as follows: ```sql INSERT INTO table2 (column1, column2, ...) SELECT column1, column2, ... FROM table1; ``` In the above syntax, `table1` is the source table from which you want to copy data, and `table2` is the target table into which the data will be inserted. The selected expressions must be assignment-compatible with the corresponding target columns. Source and target column names do not need to match. The `SELECT` statement selects the columns you want to insert from the source table. If you do not specify the column names in the `INSERT INTO` statement, then the data will be inserted into all columns in the target table. Use `INSERT INTO SELECT` to copy query results into another table. For table-level archival or copying selected data, it is more efficient than backing up and restoring the entire database. ### Examples Here is an example of an `INSERT INTO SELECT` statement: ```sql INSERT INTO system_metrics2 (host, idc, cpu_util, memory_util, disk_util, ts) SELECT host, idc, cpu_util, memory_util, disk_util, ts FROM system_metrics; ``` --- ## JOIN `JOIN` is used to combine rows from two or more tables based on a related column between them. It allows you to extract data from multiple tables and present it as a single result set. There are several types of JOIN clauses: - INNER JOIN: Returns only the rows that have matching values in both tables. - LEFT JOIN: Returns all the rows from the left table and the matching rows from the right table. - RIGHT JOIN: Returns all the rows from the right table and the matching rows from the left table. - FULL OUTER JOIN: Returns all the rows from both tables. ## Examples Here are some examples of using JOIN clauses: ```sql -- Select all rows from the system_metrics table and idc_info table where the idc_id matches SELECT a.* FROM system_metrics a JOIN idc_info b ON a.idc = b.idc_id; -- Select all rows from the idc_info table and system_metrics table where the idc_id matches, and include null values for idc_info without any matching system_metrics SELECT a.* FROM idc_info a LEFT JOIN system_metrics b ON a.idc_id = b.idc; -- Select all rows from the system_metrics table and idc_info table where the idc_id matches, and include null values for idc_info without any matching system_metrics SELECT b.* FROM system_metrics a RIGHT JOIN idc_info b ON a.idc = b.idc_id; ``` --- ## LIMIT The `LIMIT` clause sets the maximum number of rows returned by a query. It can improve query performance on large result sets by reducing the amount of data processed and returned. ## Syntax The basic syntax of the `LIMIT` clause is as follows: ```sql SELECT column1, column2, ... FROM table_name LIMIT number_of_rows; ``` `number_of_rows` must be a non-negative integer. A negative value produces a planning error. ## Examples Consider the following table named "system_metrics": ```sql +-------+-------+----------+-------------+-----------+---------------------+ | host | idc | cpu_util | memory_util | disk_util | ts | +-------+-------+----------+-------------+-----------+---------------------+ | host1 | idc_a | 11.8 | 10.3 | 10.3 | 2022-11-03 03:39:57 | | host1 | idc_b | 50 | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_c | 50.1 | 66.8 | 40.8 | 2022-11-03 03:39:57 | | host1 | idc_e | NULL | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host2 | idc_a | 80.1 | 70.3 | 90 | 2022-11-03 03:39:57 | +-------+-------+----------+-------------+-----------+---------------------+ ``` To retrieve the top three rows by `memory_util`, use the `LIMIT` clause: ```sql SELECT host, idc, memory_util FROM system_metrics ORDER BY memory_util DESC LIMIT 3; ``` The result of the above query would be: ```sql +-------+-------+-------------+ | host | idc | memory_util | +-------+-------+-------------+ | host2 | idc_a | 70.3 | | host1 | idc_c | 66.8 | | host1 | idc_b | 66.7 | +-------+-------+-------------+ ``` `LIMIT n, m` allows to select the m rows from the result after skipping the first n rows. The `LIMIT m OFFSET n` syntax is equivalent. ```sql SELECT host, idc, memory_util FROM system_metrics ORDER BY memory_util DESC LIMIT 2 OFFSET 1; ``` OR ```sql SELECT host, idc, memory_util FROM system_metrics ORDER BY memory_util DESC LIMIT 1, 2; ``` The result of the above query would be: ```sql +-------+-------+-------------+ | host | idc | memory_util | +-------+-------+-------------+ | host1 | idc_c | 66.8 | | host1 | idc_b | 66.7 | +-------+-------+-------------+ ``` --- ## OFFSET The `OFFSET` clause specifies how many rows to skip before returning rows from a query. It is commonly used with `LIMIT` for pagination. For example: ```sql SELECT * FROM system_metrics ORDER BY cpu_util DESC LIMIT 10 OFFSET 10; ``` It selects all columns from rows ranked 11th to 20th (by descending `cpu_util`) from the `system_metrics` table. The cost of `OFFSET` grows as later pages skip more rows. For large result sets, use keyset pagination with an ordering that uniquely identifies each row. A timestamp alone is not sufficient when multiple rows can have the same timestamp. ## Efficient Pagination Using Timestamps Suppose `(ts, host, idc)` uniquely orders the rows in `system_metrics`. Sort all three columns and record their values from the last row of each page. First Page (Latest 10 Records): ```sql SELECT * FROM system_metrics ORDER BY ts DESC, host DESC, idc DESC LIMIT 10; ``` If the last row of the first page is `('2024-07-01 16:03:00', 'host2', 'idc_b')`, fetch the next page with a composite cursor: ```sql SELECT * FROM system_metrics WHERE ts < '2024-07-01 16:03:00' OR (ts = '2024-07-01 16:03:00' AND host < 'host2') OR (ts = '2024-07-01 16:03:00' AND host = 'host2' AND idc < 'idc_b') ORDER BY ts DESC, host DESC, idc DESC LIMIT 10; ``` Use cursor columns that are stable and unique for the query. If `(ts, host, idc)` is not unique, add another tie-breaker column to both the `ORDER BY` clause and cursor predicate. This method avoids repeatedly scanning and skipping earlier pages, making pagination more efficient on large result sets. --- ## ORDER BY The `ORDER BY` clause is used to order the data in ascending or descending order based on one or more columns in the `SELECT` statement. ## Syntax The basic syntax of the `ORDER BY` clause is as follows: ```sql SELECT column1, column2, ... FROM table_name ORDER BY column1 [ASC | DESC], column2 [ASC | DESC], ...; ``` The `ORDER BY` clause can be used with one or more columns. The ASC keyword is used to sort the data in ascending order (default), and the DESC keyword is used to sort the data in descending order. ## Examples Consider the following table named "system_metrics": ```sql +-------+-------+----------+-------------+-----------+---------------------+ | host | idc | cpu_util | memory_util | disk_util | ts | +-------+-------+----------+-------------+-----------+---------------------+ | host1 | idc_a | 11.8 | 10.3 | 10.3 | 2022-11-03 03:39:57 | | host1 | idc_b | 50 | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_c | 50.1 | 66.8 | 40.8 | 2022-11-03 03:39:57 | | host1 | idc_e | NULL | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host2 | idc_a | 80.1 | 70.3 | 90 | 2022-11-03 03:39:57 | +-------+-------+----------+-------------+-----------+---------------------+ ``` To sort the data in ascending order based on the "memory_util" column, the following SQL query can be used: ```sql SELECT * FROM system_metrics ORDER BY memory_util ASC; ``` The result of the above query would be: ```sql +-------+-------+----------+-------------+-----------+---------------------+ | host | idc | cpu_util | memory_util | disk_util | ts | +-------+-------+----------+-------------+-----------+---------------------+ | host1 | idc_a | 11.8 | 10.3 | 10.3 | 2022-11-03 03:39:57 | | host1 | idc_b | 50 | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_e | NULL | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_c | 50.1 | 66.8 | 40.8 | 2022-11-03 03:39:57 | | host2 | idc_a | 80.1 | 70.3 | 90 | 2022-11-03 03:39:57 | +-------+-------+----------+-------------+-----------+---------------------+ ``` To sort the data in descending order based on the "disk_util" column, the following SQL query can be used: ```sql SELECT * FROM system_metrics ORDER BY disk_util DESC; ``` The result of the above query would be: ```sql +-------+-------+----------+-------------+-----------+---------------------+ | host | idc | cpu_util | memory_util | disk_util | ts | +-------+-------+----------+-------------+-----------+---------------------+ | host2 | idc_a | 80.1 | 70.3 | 90 | 2022-11-03 03:39:57 | | host1 | idc_c | 50.1 | 66.8 | 40.8 | 2022-11-03 03:39:57 | | host1 | idc_b | 50 | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_e | NULL | 66.7 | 40.6 | 2022-11-03 03:39:57 | | host1 | idc_a | 11.8 | 10.3 | 10.3 | 2022-11-03 03:39:57 | +-------+-------+----------+-------------+-----------+---------------------+ ``` --- ## SQL(Sql) ## Data Types * [Data Types](data-types.md) ## Statements and Clauses * [ADMIN](./admin.md) * [ALTER](./alter.md) * [ANSI Compatibility](./compatibility.md) * [CASE](./case.md) * [CAST](./cast.md) * [COPY](./copy.md) * [CREATE](./create.md) * [DELETE](./delete.md) * [DESCRIBE TABLE](./describe_table.md) * [DISTINCT](./distinct.md) * [DROP](./drop.md) * [EXPLAIN](./explain.md) * [GROUP BY](./group_by.md) * [HAVING](./having.md) * [INSERT](./insert.md) * [JOIN](./join.md) * [LIMIT](./limit.md) * [OFFSET](./offset.md) * [ORDER BY](./order_by.md) * [RANGE](./range.md) * [REPLACE](./replace.md) * [SELECT](./select.md) * [SHOW](./show.md) * [TQL](./tql.md) * [TRUNCATE](./truncate.md) * [WHERE](./where.md) * [WITH](./with.md) ## Functions * [Functions](./functions/overview.md) ## Information Schema * [INFORMATION_SCHEMA](./information-schema/overview.md) ## Greptime Private * [Greptime Private](./greptime-private/overview.md) --- ## RANGE QUERY A Range query divides time-series data into time windows and evaluates aggregate expressions over each window. It is part of a `SELECT` statement and is similar to a PromQL range selector. ## Syntax Range query uses `Time Index` column as the timeline basis for aggregation. A legal Range query syntax structure is as follows: ```sql SELECT AGGR_FUNCTION(column1, column2,..) RANGE INTERVAL [FILL FILL_OPTION], ... FROM table_name [ WHERE ] ALIGN INTERVAL [ TO TO_OPTION ] [BY (columna, columnb,..)] [FILL FILL_OPTION] [ ORDER BY ] [ LIMIT ]; INTERVAL := TIME_INTERVAL | ( INTERVAL expr ) ``` - Keyword `ALIGN`, required field, followed by parameter `INTERVAL`, `ALIGN` specifies the step of Range query. - Subkeyword `TO`, optional field, specifies the time point to which Range query is aligned. For legal `TO_OPTION` parameters, see [TO Option](#to-option). - Subkeyword `BY`, optional field, followed by parameter `(columna, columnb,..)`, describes the aggregate key. For legal `BY_OPTION` parameters, see [BY Option](#by-option). - The parameter `INTERVAL` is mainly used to give the length of a period of time. There are two parameter forms: - Strings based on the `PromQL Time Durations` format (eg: `3h`, `1h30m`). Visit the [Prometheus documentation](https://prometheus.io/docs/prometheus/latest/querying/basics/#float-literals-and-time-durations) for a more detailed description of this format. - `Interval` type. To use the `Interval` type, you need to carry parentheses, (for example: `('1 year 3 hours 20 minutes'::INTERVAL)`). Visit [Interval](./data-types.md#interval-type) for a more detailed description of this format. - `AGGR_FUNCTION(column1, column2,..) RANGE INTERVAL [FILL FILL_OPTION]` is called a Range expression. - `AGGR_FUNCTION(column1, column2,..)` is an aggregate function that represents the expression that needs to be aggregated. - Keyword `RANGE`, required field, followed by parameter `INTERVAL` specifies the time range of each data aggregation. - Keyword `FILL`, optional field, please see [`FILL` Option](#fill-option) for details. - Range expressions can be combined with other operations to implement more complex queries. For details, see [Nested Range Expressions](#nested-range-expressions). - `FILL` keyword after `ALIGN`, optional field. See [FILL Option](#fill-option) for details. ## `FILL` Option The `FILL` option specifies the data filling method when there is no data in an aggregated time slot, or the value of the aggregate field is empty. The `FILL` keyword can be used after the `RANGE` keyword to indicate the filling method for the Range expression. The `FILL` keyword can also be used after the `ALIGN` keyword to specify the default filling method for a Range expression if no fill option is provided. For example, in the following SQL code, the `max(val) RANGE '10s'` Range expression uses the fill option `LINEAR`, while the `min(val) RANGE '10s'` Range expression, which does not specify a fill option, uses the fill option `PREV` specified after the `ALIGN` keyword. ```sql SELECT ts, host, min(val) RANGE '10s', max(val) RANGE '10s' FILL LINEAR FROM host_val ALIGN '5s' BY (host) FILL PREV; ``` `FILL` has the following filling methods: | FILL | DESCRIPTION | | :------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | `NULL` | Fill directly with `NULL` | | `PREV` | Fill with data from previous point | | `LINEAR` | Use [linear interpolation](https://en.wikipedia.org/wiki/Linear_interpolation) to fill the data. If an integer type is filled with `LINEAR`, the variable type of the column will be implicitly converted to a floating point type during calculation | | `X` | Fill in a constant, the data type of the constant must be consistent with the variable type of the Range expression | Take the following table as an example: ```sql DROP TABLE IF EXISTS host_val; CREATE TABLE host_val ( ts TIMESTAMP TIME INDEX, host STRING, val DOUBLE, PRIMARY KEY (host) ); INSERT INTO host_val VALUES ('1970-01-01 00:00:00', 'host1', 0), ('1970-01-01 00:00:15', 'host1', 6), ('1970-01-01 00:00:00', 'host2', 6), ('1970-01-01 00:00:15', 'host2', 12); ``` ```sql +---------------------+-------+------+ | ts | host | val | +---------------------+-------+------+ | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:15 | host1 | 6 | | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:15 | host2 | 12 | +---------------------+-------+------+ ``` The result of each `FILL` option is as follows: ```sql SELECT ts, host, min(val) RANGE '5s' FROM host_val ALIGN '5s'; ``` ```sql +---------------------+-------+----------------------------+ | ts | host | min(host_val.val) RANGE 5s | +---------------------+-------+----------------------------+ | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:15 | host1 | 6 | | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:15 | host2 | 12 | +---------------------+-------+----------------------------+ ``` ```sql SELECT ts, host, min(val) RANGE '5s' FILL NULL FROM host_val ALIGN '5s'; ``` ```sql +---------------------+-------+--------------------------------------+ | ts | host | min(host_val.val) RANGE 5s FILL NULL | +---------------------+-------+--------------------------------------+ | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:05 | host2 | NULL | | 1970-01-01 00:00:10 | host2 | NULL | | 1970-01-01 00:00:15 | host2 | 12 | | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:05 | host1 | NULL | | 1970-01-01 00:00:10 | host1 | NULL | | 1970-01-01 00:00:15 | host1 | 6 | +---------------------+-------+--------------------------------------+ ``` ```sql SELECT ts, host, min(val) RANGE '5s' FILL PREV FROM host_val ALIGN '5s'; ``` ```sql +---------------------+-------+--------------------------------------+ | ts | host | min(host_val.val) RANGE 5s FILL PREV | +---------------------+-------+--------------------------------------+ | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:05 | host1 | 0 | | 1970-01-01 00:00:10 | host1 | 0 | | 1970-01-01 00:00:15 | host1 | 6 | | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:05 | host2 | 6 | | 1970-01-01 00:00:10 | host2 | 6 | | 1970-01-01 00:00:15 | host2 | 12 | +---------------------+-------+--------------------------------------+ ``` ```sql SELECT ts, host, min(val) RANGE '5s' FILL LINEAR FROM host_val ALIGN '5s'; ``` ```sql +---------------------+-------+----------------------------------------+ | ts | host | min(host_val.val) RANGE 5s FILL LINEAR | +---------------------+-------+----------------------------------------+ | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:05 | host2 | 8 | | 1970-01-01 00:00:10 | host2 | 10 | | 1970-01-01 00:00:15 | host2 | 12 | | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:05 | host1 | 2 | | 1970-01-01 00:00:10 | host1 | 4 | | 1970-01-01 00:00:15 | host1 | 6 | +---------------------+-------+----------------------------------------+ ``` ```sql [FILL Constant Value 6.0] SELECT ts, host, min(val) RANGE '5s' FILL 6 FROM host_val ALIGN '5s'; ``` ```sql +---------------------+-------+-----------------------------------+ | ts | host | min(host_val.val) RANGE 5s FILL 6 | +---------------------+-------+-----------------------------------+ | 1970-01-01 00:00:00 | host2 | 6 | | 1970-01-01 00:00:05 | host2 | 6 | | 1970-01-01 00:00:10 | host2 | 6 | | 1970-01-01 00:00:15 | host2 | 12 | | 1970-01-01 00:00:00 | host1 | 0 | | 1970-01-01 00:00:05 | host1 | 6 | | 1970-01-01 00:00:10 | host1 | 6 | | 1970-01-01 00:00:15 | host1 | 6 | +---------------------+-------+-----------------------------------+ ``` If there are multiple Range expressions but only one of them specifies the `Fill` option, the other Range expressions will use the `FILL NULL` method to fill in the missing time slots. The following two SQL statements are equivalent in output: ```sql SELECT ts, host, min(val) RANGE '10s', max(val) RANGE '10s' FILL LINEAR FROM host_val ALIGN '5s'; ``` ```sql SELECT ts, host, min(val) RANGE '10s' FILL NULL, max(val) RANGE '10s' FILL LINEAR FROM host_val ALIGN '5s'; ``` The result of the above SQL is as follows: ```sql +---------------------+-------+---------------------------------------+-----------------------------------------+ | ts | host | min(host_val.val) RANGE 10s FILL NULL | max(host_val.val) RANGE 10s FILL LINEAR | +---------------------+-------+---------------------------------------+-----------------------------------------+ | 1969-12-31 23:59:55 | host1 | 0 | 0 | | 1970-01-01 00:00:00 | host1 | 0 | 0 | | 1970-01-01 00:00:05 | host1 | NULL | 2.9999999999999996 | | 1970-01-01 00:00:10 | host1 | 6 | 6 | | 1970-01-01 00:00:15 | host1 | 6 | 6 | | 1969-12-31 23:59:55 | host2 | 6 | 6 | | 1970-01-01 00:00:00 | host2 | 6 | 6 | | 1970-01-01 00:00:05 | host2 | NULL | 9 | | 1970-01-01 00:00:10 | host2 | 12 | 12 | | 1970-01-01 00:00:15 | host2 | 12 | 12 | +---------------------+-------+---------------------------------------+-----------------------------------------+ ``` ## `TO` Option The `TO` keyword specifies the origin time point to which the range query is aligned. `TO` option along with `RANGE` option and `ALIGN INTERVAL` determine the time range windows. Please refer to [Time Range Window](/user-guide/query-data/sql.md#time-range-window) for details. The default value of the `TO` option is the current query client timezone. To set the timezone, please refer to [MySQL client](/user-guide/protocols/mysql.md#time-zone) or [PostgreSQL client](/user-guide/protocols/postgresql.md#time-zone). Other valid `TO` options are: | TO | DESCRIPTION | | :---------: | :----------------------------------------------------------------------------------: | | `NOW` | Align to current query time | | `Timestamp` | Align to a user-specified timestamp, supports timestamp format `RFC3339` / `ISO8601` | Suppose we have a table `host_val` with the following data: ```sql DROP TABLE IF EXISTS host_val; CREATE TABLE host_val ( ts TIMESTAMP TIME INDEX, host STRING, val DOUBLE, PRIMARY KEY (host) ); INSERT INTO host_val VALUES ('2023-01-01 23:00:00', 'host1', 0), ('2023-01-02 01:00:00', 'host1', 1), ('2023-01-01 23:00:00', 'host2', 2), ('2023-01-02 01:00:00', 'host2', 3); ``` ```sql +---------------------+-------+------+ | ts | host | val | +---------------------+-------+------+ | 2023-01-01 23:00:00 | host1 | 0 | | 2023-01-02 01:00:00 | host1 | 1 | | 2023-01-01 23:00:00 | host2 | 2 | | 2023-01-02 01:00:00 | host2 | 3 | +---------------------+-------+------+ ``` The query results by each `TO` options shown below: ```sql -- Querying the database timezone using the MySQL protocol, currently in the UTC timezone SELECT @@time_zone; ``` ```sql +-------------+ | @@time_zone | +-------------+ | UTC | +-------------+ ``` ```sql -- If we do not specify the `TO` keyword, -- the timezone will be used as the default origin alignment time. SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d'; ``` ```sql +---------------------+-------+----------------------------+ | ts | host | min(host_val.val) RANGE 1d | +---------------------+-------+----------------------------+ | 2023-01-01 00:00:00 | host1 | 0 | | 2023-01-02 00:00:00 | host1 | 1 | | 2023-01-01 00:00:00 | host2 | 2 | | 2023-01-02 00:00:00 | host2 | 3 | +---------------------+-------+----------------------------+ ``` ```sql -- If you want to align the origin time to the current time, -- use the `NOW` keyword. -- Assume that the current query time is `2023-01-02T09:16:40.503000`. SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d' TO NOW; ``` ```sql +----------------------------+-------+----------------------------+ | ts | host | min(host_val.val) RANGE 1d | +----------------------------+-------+----------------------------+ | 2023-01-01 09:54:55.456000 | host1 | 0 | | 2023-01-01 09:54:55.456000 | host2 | 2 | +----------------------------+-------+----------------------------+ ``` ```sql -- If you want to align the origin time to a specific timestamp, -- for example, "+08:00" Beijing time on December 1, 2023, -- you can set the `TO` option to the specific timestamp '2023-01-01T00:00:00+08:00'. SELECT ts, host, min(val) RANGE '1d' FROM host_val ALIGN '1d' TO '2023-01-01T00:00:00+08:00'; ``` ```sql +---------------------+-------+----------------------------+ | ts | host | min(host_val.val) RANGE 1d | +---------------------+-------+----------------------------+ | 2023-01-01 16:00:00 | host1 | 0 | | 2023-01-01 16:00:00 | host2 | 2 | +---------------------+-------+----------------------------+ ``` If you want to query data for a specific time range, you can specify the timestamp using the `TO` keyword. For example, to query the daily minimum value of `val` between `00:45` and `06:45`, you can use `2023-01-01T00:45:00` as the `TO` option along with a `6h` range. ```sql SELECT ts, host, min(val) RANGE '6h' FROM host_val ALIGN '1d' TO '2023-01-01T00:45:00'; ``` ```sql +---------------------+-------+----------------------------+ | ts | host | min(host_val.val) RANGE 6h | +---------------------+-------+----------------------------+ | 2023-01-02 00:45:00 | host2 | 3 | | 2023-01-02 00:45:00 | host1 | 1 | +---------------------+-------+----------------------------+ ``` ## `BY` Option `BY` option describes the aggregate key. If this field is not given, the primary key of the table is used as the aggregate key by default. If the table does not specify a primary key, the `BY` keyword cannot be omitted. Suppose we have a tale `host_val` with the following data: ```sql DROP TABLE IF EXISTS host_val; CREATE TABLE host_val ( ts TIMESTAMP TIME INDEX, host STRING, val DOUBLE, PRIMARY KEY (host) ); INSERT INTO host_val VALUES ('2023-01-01 23:00:00', 'host1', 0), ('2023-01-02 01:00:00', 'host1', 1), ('2023-01-01 23:00:00', 'host2', 2), ('2023-01-02 01:00:00', 'host2', 3); ``` ```sql +---------------------+-------+------+ | ts | host | val | +---------------------+-------+------+ | 2023-01-01 23:00:00 | host1 | 0 | | 2023-01-02 01:00:00 | host1 | 1 | | 2023-01-01 23:00:00 | host2 | 2 | | 2023-01-02 01:00:00 | host2 | 3 | +---------------------+-------+------+ ``` The following SQL uses `host` as the aggregate key: ```sql SELECT ts, host, min(val) RANGE '10s' FROM host_val ALIGN '5s' BY (host); ``` ```sql +---------------------+-------+-----------------------------+ | ts | host | min(host_val.val) RANGE 10s | +---------------------+-------+-----------------------------+ | 2023-01-01 22:59:55 | host1 | 0 | | 2023-01-01 23:00:00 | host1 | 0 | | 2023-01-02 00:59:55 | host1 | 1 | | 2023-01-02 01:00:00 | host1 | 1 | | 2023-01-01 22:59:55 | host2 | 2 | | 2023-01-01 23:00:00 | host2 | 2 | | 2023-01-02 00:59:55 | host2 | 3 | | 2023-01-02 01:00:00 | host2 | 3 | +---------------------+-------+-----------------------------+ ``` You can also use the `BY` keyword to declare other columns as the basis for data aggregation. For example, the following RANGE query uses the string length `length(host)` of the `host` column as the basis for data aggregation. ```sql SELECT ts, length(host), min(val) RANGE '10s' FROM host_val ALIGN '5s' BY (length(host)); ``` Get after running ```sql +---------------------+---------------------------------+-----------------------------+ | ts | character_length(host_val.host) | min(host_val.val) RANGE 10s | +---------------------+---------------------------------+-----------------------------+ | 2023-01-01 22:59:55 | 5 | 0 | | 2023-01-01 23:00:00 | 5 | 0 | | 2023-01-02 00:59:55 | 5 | 1 | | 2023-01-02 01:00:00 | 5 | 1 | +---------------------+---------------------------------+-----------------------------+ ``` You can explicitly use `BY ()`, which means you do not need to use aggregation keys and aggregate all data into a group. **However, if you omit the `BY` keyword directly, it means using the primary key of the table as the aggregate key.** ```sql SELECT ts, min(val) RANGE '10s' FROM host_val ALIGN '5s' BY (); ``` Get after running ```sql +---------------------+-----------------------------+ | ts | min(host_val.val) RANGE 10s | +---------------------+-----------------------------+ | 2023-01-01 22:59:55 | 0 | | 2023-01-01 23:00:00 | 0 | | 2023-01-02 00:59:55 | 1 | | 2023-01-02 01:00:00 | 1 | +---------------------+-----------------------------+ ``` ## `ORDER BY` option in aggregate functions Range queries support the use of `order by` expressions in the parameters of the `first_value` and `last_value` aggregate functions. By default, the data is sorted in ascending order based on the time index column. Take this table as an example: ```sql DROP TABLE IF EXISTS host_val; CREATE TABLE host_val ( ts TIMESTAMP TIME INDEX, host STRING, val DOUBLE, addon DOUBLE, PRIMARY KEY (host) ); INSERT INTO host_val VALUES ('1970-01-01 00:00:00', 'host1', 0, 3), ('1970-01-01 00:00:01', 'host1', 1, 2), ('1970-01-01 00:00:02', 'host1', 2, 1); ``` ```sql +---------------------+-------+------+-------+ | ts | host | val | addon | +---------------------+-------+------+-------+ | 1970-01-01 00:00:00 | host1 | 0 | 3 | | 1970-01-01 00:00:01 | host1 | 1 | 2 | | 1970-01-01 00:00:02 | host1 | 2 | 1 | +---------------------+-------+------+-------+ ``` When the `order by` expression is not specified in the function parameter, the default behavior is to sort the data in ascending order based on the time index column. The following two SQL statements are equivalent: ```sql SELECT ts, first_value(val) RANGE '5s', last_value(val) RANGE '5s' FROM host_val ALIGN '5s'; ``` ```sql SELECT ts, first_value(val order by ts ASC) RANGE '5s', last_value(val order by ts ASC) RANGE '5s' FROM host_val ALIGN '5s'; ``` Get after query ```sql +---------------------+------------------------------------+-----------------------------------+ | ts | first_value(host_val.val) RANGE 5s | last_value(host_val.val) RANGE 5s | +---------------------+------------------------------------+-----------------------------------+ | 1970-01-01 00:00:00 | 0 | 2 | +---------------------+------------------------------------+-----------------------------------+ ``` You can specify your own sorting rules. For example, the following SQL sorts the data by the `addon` column in ascending order: ```sql SELECT ts, first_value(val ORDER BY addon ASC) RANGE '5s', last_value(val ORDER BY addon ASC) RANGE '5s' FROM host_val ALIGN '5s'; ``` Get after query ```sql +---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+ | ts | first_value(host_val.val) ORDER BY [host_val.addon ASC NULLS LAST] RANGE 5s | last_value(host_val.val) ORDER BY [host_val.addon ASC NULLS LAST] RANGE 5s | +---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+ | 1970-01-01 00:00:00 | 2 | 0 | +---------------------+-----------------------------------------------------------------------------+----------------------------------------------------------------------------+ ``` ## Nested Range Expressions A Range expression can appear inside a larger arithmetic expression, but one Range expression cannot contain another Range expression. Take the following table as an example: ```sql DROP TABLE IF EXISTS host_val; CREATE TABLE host_val ( ts TIMESTAMP TIME INDEX, host STRING, val DOUBLE, PRIMARY KEY (host) ); INSERT INTO host_val VALUES ('2023-01-01 08:00:00', 'host1', 1.1), ('2023-01-01 08:00:05', 'host1', 2.2), ('2023-01-01 08:00:00', 'host2', 3.3), ('2023-01-01 08:00:05', 'host2', 4.4); ``` ```sql +---------------------+-------+------+ | ts | host | val | +---------------------+-------+------+ | 2023-01-01 08:00:00 | host1 | 1.1 | | 2023-01-01 08:00:05 | host1 | 2.2 | | 2023-01-01 08:00:00 | host2 | 3.3 | | 2023-01-01 08:00:05 | host2 | 4.4 | +---------------------+-------+------+ ``` 1. Aggregation functions support calculations both internally and externally: ```sql SELECT ts, host, 2.0 * min(val * 2.0) RANGE '10s' FROM host_val ALIGN '5s'; ``` Get after running ```sql +---------------------+-------+-------------------------------------------------------+ | ts | host | Float64(2) * min(host_val.val * Float64(2)) RANGE 10s | +---------------------+-------+-------------------------------------------------------+ | 2023-01-01 07:59:55 | host1 | 4.4 | | 2023-01-01 08:00:00 | host1 | 4.4 | | 2023-01-01 08:00:05 | host1 | 8.8 | | 2023-01-01 07:59:55 | host2 | 13.2 | | 2023-01-01 08:00:00 | host2 | 13.2 | | 2023-01-01 08:00:05 | host2 | 17.6 | +---------------------+-------+-------------------------------------------------------+ ``` 2. Scalar functions are supported both inside and outside aggregate functions: - `min(round(val)) RANGE '10s'` means that each value is rounded using the `round` function before aggregation - `round(min(val) RANGE '10s')` means rounding the result of each aggregation using the `round` function ```sql SELECT ts, host, min(round(val)) RANGE '10s' FROM host_val ALIGN '5s'; ``` Get after running ```sql +---------------------+-------+------------------------------------+ | ts | host | min(round(host_val.val)) RANGE 10s | +---------------------+-------+------------------------------------+ | 2023-01-01 07:59:55 | host1 | 1 | | 2023-01-01 08:00:00 | host1 | 1 | | 2023-01-01 08:00:05 | host1 | 2 | | 2023-01-01 07:59:55 | host2 | 3 | | 2023-01-01 08:00:00 | host2 | 3 | | 2023-01-01 08:00:05 | host2 | 4 | +---------------------+-------+------------------------------------+ ``` ```sql SELECT ts, host, round(min(val) RANGE '10s') FROM host_val ALIGN '5s'; ``` Get after running ```sql +---------------------+-------+------------------------------------+ | ts | host | round(min(host_val.val) RANGE 10s) | +---------------------+-------+------------------------------------+ | 2023-01-01 07:59:55 | host2 | 3 | | 2023-01-01 08:00:00 | host2 | 3 | | 2023-01-01 08:00:05 | host2 | 4 | | 2023-01-01 07:59:55 | host1 | 1 | | 2023-01-01 08:00:00 | host1 | 1 | | 2023-01-01 08:00:05 | host1 | 2 | +---------------------+-------+------------------------------------+ ``` 3. Multiple Range expressions can also evaluate together, and Range expressions support the distributive law. The following two Range query are legal and equivalent: ```sql SELECT ts, host, max(val) RANGE '10s' - min(val) RANGE '10s' FROM host_val ALIGN '5s'; ``` ```sql SELECT ts, host, (max(val) - min(val)) RANGE '10s' FROM host_val ALIGN '5s'; ``` Get after running ```sql +---------------------+-------+-----------------------------------------------------------+ | ts | host | max(host_val.val) RANGE 10s - min(host_val.val) RANGE 10s | +---------------------+-------+-----------------------------------------------------------+ | 2023-01-01 07:59:55 | host2 | 0 | | 2023-01-01 08:00:00 | host2 | 1.1000000000000005 | | 2023-01-01 08:00:05 | host2 | 0 | | 2023-01-01 07:59:55 | host1 | 0 | | 2023-01-01 08:00:00 | host1 | 1.1 | | 2023-01-01 08:00:05 | host1 | 0 | +---------------------+-------+-----------------------------------------------------------+ ``` But note that the `RANGE` keyword apply to the expression before the `RANGE` keyword. The following Range query is illegal because the `RANGE` keyword apply to the expression `2.0`, not the expression `min(val * 2.0) * 2.0` ```sql SELECT ts, host, min(val * 2.0) * 2.0 RANGE '10s' FROM host_val ALIGN '5s'; ``` ```sql ERROR 1815 (HY000): sql parser error: Can't use the RANGE keyword in Expr 2.0 without function ``` Expressions can be bracketed, and the `RANGE` keyword is automatically applied to any aggregate functions contained within the brackets: ```sql SELECT ts, host, (min(val * 2.0) * 2.0) RANGE '10s' FROM host_val ALIGN '5s'; ``` After running, we get: ```sql +---------------------+-------+-------------------------------------------------------+ | ts | host | min(host_val.val * Float64(2)) RANGE 10s * Float64(2) | +---------------------+-------+-------------------------------------------------------+ | 2023-01-01 07:59:55 | host2 | 13.2 | | 2023-01-01 08:00:00 | host2 | 13.2 | | 2023-01-01 08:00:05 | host2 | 17.6 | | 2023-01-01 07:59:55 | host1 | 4.4 | | 2023-01-01 08:00:00 | host1 | 4.4 | | 2023-01-01 08:00:05 | host1 | 8.8 | +---------------------+-------+-------------------------------------------------------+ ``` Nesting of Range expressions is not allowed. Nested Range queries are illegal: ```sql SELECT ts, host, max(min(val) RANGE '10s') RANGE '10s' FROM host_val ALIGN '5s'; ``` ```sql ERROR 1815 (HY000): Range Query: Nest Range Query is not allowed ``` --- ## REPLACE The `REPLACE` statement is used to insert new records into a table. In GreptimeDB, this statement is exactly the same as the `INSERT` statement. Please refer to [`INSERT`](/reference/sql/insert.md) for more details. ## `REPLACE INTO` Statement ### Syntax The syntax for the REPLACE INTO statement is as follows: ```sql REPLACE INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...); ``` ### Examples Here is an example of an `REPLACE INTO` statement that inserts a record into a table named `system_metrics`: ```sql REPLACE INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_b", 50.0, 66.7, 40.6, 1667446797462); ``` Here is an example of an `REPLACE INTO` statement that inserts multiple records into the `system_metrics` table: ```sql REPLACE INTO system_metrics (host, idc, cpu_util, memory_util, disk_util, ts) VALUES ("host1", "idc_a", 11.8, 10.3, 10.3, 1667446797460), ("host2", "idc_a", 80.1, 70.3, 90.0, 1667446797461), ("host1", "idc_c", 50.1, 66.8, 40.8, 1667446797463); ``` --- ## SELECT The `SELECT` statement is the foundation of data retrieval in SQL and GreptimeDB. It allows you to extract specific columns or expressions from one or more tables: ## Basic Syntax The basic syntax for a `SELECT` statement is as follows: ```sql SELECT column1, column2, ... FROM table_name [WHERE condition] [GROUP BY column] [HAVING condition] [ORDER BY column] [LIMIT number] [OFFSET number] ``` Here, column1, column2, etc. refer to the names of the columns from which we want to retrieve data, and table_name refers to the name of the table from which we want to retrieve the data. This statement selects all the columns from the table specified in the `FROM` clause. If you want to select all columns from the table, you can use the asterisk (*) wildcard character instead of listing individual columns. ```sql SELECT * FROM table_name; ``` ## Conditional Filtering (WHERE clause) The `WHERE` clause is used to filter the results of a `SELECT` statement based on a specified condition. The syntax for using WHERE clause is as follows: ```sql SELECT column1, column2, ..., columnN FROM table_name WHERE condition; ``` Here, the condition is an expression that evaluates to either true or false. Only the rows that satisfy the condition will be included in the result set. Supported comparisons include: * Logical operators: `AND`, `OR`, `NOT` * Comparison operators: `=`, `!=`, `>`, `<`, `>=`, `<=` * Pattern matching: `LIKE`, `IN`, `BETWEEN` For example: ```sql -- Select all rows from the system_metrics table where idc is 'idc0' SELECT * FROM system_metrics WHERE idc = 'idc0'; -- Select all rows from the system_metrics table where idc is 'idc0' or 'idc1' SELECT * FROM system_metrics WHERE idc IN ('idc0', 'idc1'); -- Select all rows where idc is 'idc0' or 'idc1' and cpu utilization is greater than 60% SELECT * FROM system_metrics WHERE idc IN ('idc0', 'idc1') AND cpu_util > 0.6; ``` Please refer to [WHERE](where.md) for more information. ## Order Results(ORDER BY clause) The `ORDER BY` clause is used to order the data in ascending or descending order based on one or more columns in the SELECT statement. For example: ```sql -- Sort the results by cpu_util in ascending order SELECT * FROM system_metrics ORDER BY cpu_util ASC; -- Sort the results by cpu_util in descending order SELECT * FROM system_metrics ORDER BY cpu_util DESC; ``` Please refer to [ORDER](order_by.md) for more information. ## Limiting Results (LIMIT clause) The `LIMIT` clause is used to limit the number of rows returned by a SELECT statement. The syntax for using `LIMIT` clause is as follows: ```sql SELECT column1, column2, ... FROM table_name LIMIT number_of_rows; ``` Here, the number_of_rows parameter specifies the maximum number of rows to return. Here are some examples of using the `LIMIT` clause: ```sql -- Select the top 10 high cpu rows from the system_metrics table SELECT * FROM system_metrics ORDER BY cpu_util DESC LIMIT 10; ``` ## Pagination Results (LIMIT and OFFSET) The `OFFSET` clause specifies how many rows to skip before starting to return rows from the query. It's commonly used with LIMIT for paginating through large result sets. For example: ```sql SELECT * FROM system_metrics ORDER BY cpu_util DESC LIMIT 10 OFFSET 10; ``` It selects all columns from rows ranked 11th to 20th (by descending `cpu_util`) from the `system_metrics` table. The cost of `OFFSET` grows as later pages skip more rows. For keyset pagination with a stable, unique cursor, see [OFFSET](offset.md#efficient-pagination-using-timestamps). ## Joining Tables (JOIN) The `JOIN` clause is used to combine rows from two or more tables based on a related column between them. The syntax for using `JOIN` clause is as follows: ```sql SELECT column1, column2, ... FROM table1 JOIN table2 ON table1.column = table2.column; ``` Here, the table1 and table2 are the names of the tables to be joined. The column is the related column between the two tables. Please refer to [JOIN](join.md) for more information. ## Grouping and Aggregation (GROUP BY and Aggregate Functions) Use `GROUP BY` to cluster data by one or more columns and perform calculations like counting or averaging within those groups. ```sql SELECT column1, column2, ..., aggregate_function(column) FROM table_name GROUP BY column1, column2, ...; ``` Common aggregate functions supported: * COUNT * SUM * AVG * MAX * MIN For more functions, see [Aggregate Functions](/reference/sql/functions/df-functions.md#aggregate-functions) and [Window Functions](/reference/sql/functions/df-functions.md#window-functions). For example: ```sql -- Select the total number of idc for each idc SELECT idc, COUNT(host) as host_num FROM system_metrics GROUP BY idc; -- Select the idc's average cpu_util SELECT idc, AVG(cpu_util) as cpu_avg FROM system_metrics GROUP BY idc; ``` Please refer to [GROUP BY](group_by.md) for more information. ## Filtering Groups (HAVING clause) The `HAVING` clause allows you to filter grouped (aggregated) results—it works like `WHERE`, but after grouping has taken place. Example: ```sql SELECT DATE_TRUNC('day', event_time) AS log_date, COUNT(*) AS error_count FROM application_logs WHERE log_level = 'ERROR' GROUP BY log_date HAVING error_count > 10; ``` Explanation: * Groups logs by day and counts occurrences where the log level is `'ERROR'`. * Only dates where the number of errors exceeds 10 are returned. Please refer to [HAVING](having.md) for more information. ## RANGE query ```sql SELECT ts, host, min(cpu) RANGE '10s', max(cpu) RANGE '10s' FILL LINEAR FROM host_cpu ALIGN '5s' BY (host) FILL PREV; ``` Please refer to [RANGE QUERY](range.md) for more information. --- ## SHOW The `SHOW` keyword provides database and table information. ## SHOW DATABASES Show all databases: ```sql SHOW [FULL] DATABASES; ``` ```sql +----------+ | Database | +----------+ | public | +----------+ 1 row in set (0.01 sec) ``` Show databases by `LIKE` pattern: ```sql SHOW DATABASES LIKE 'p%'; ``` Show databases by `where` expr: ```sql SHOW DATABASES WHERE Database='test_public_schema'; ``` Show all databases with options: ```sql CREATE DATABASE test WITH(ttl='7d'); SHOW FULL DATABASES; ``` ```sql +--------------------+-------------+ | Database | Options | +--------------------+-------------+ | greptime_private | | | information_schema | | | public | | | test | ttl='7days' | +--------------------+-------------+ ``` ## SHOW CREATE DATABASE Shows the `CREATE DATABASE` statement that creates the named database: ```sql SHOW CREATE DATABASE test; ``` ```sql +----------+------------------------------------------------------------+ | Database | Create Database | +----------+------------------------------------------------------------+ | test | CREATE DATABASE IF NOT EXISTS test WITH( ttl = '7days' ) | +----------+------------------------------------------------------------+ 1 row in set (0.01 sec) ``` ## SHOW TABLES Show all tables: ```sql SHOW TABLES; ``` ```sql +---------+ | Tables | +---------+ | numbers | | scripts | +---------+ 2 rows in set (0.00 sec) ``` Show tables in the `test` database: ```sql SHOW TABLES FROM test; ``` Show tables by `like` pattern: ```sql SHOW TABLES like '%prometheus%'; ``` Show tables by `where` expr: ```sql SHOW TABLES FROM test WHERE Tables='numbers'; ``` ## SHOW FULL TABLES ```sql SHOW FULL TABLES [IN | FROM] [DATABASE] [LIKE pattern] [WHERE query] ``` It will list all tables and table types in the database: ```sql SHOW FULL TABLES; ``` ```sql +---------+------------+ | Tables | Table_type | +---------+------------+ | monitor | BASE TABLE | | numbers | TEMPORARY | +---------+------------+ 2 rows in set (0.00 sec) ``` * `Tables`: the table names. * `Table_type`: the table types, such as `BASE_TABLE`, `TEMPORARY`, and `VIEW` etc. It supports `like` and `where` query too: ```sql SHOW FULL TABLES FROM public like '%mo%'; ``` ```sql +---------+------------+ | Tables | Table_type | +---------+------------+ | monitor | BASE TABLE | +---------+------------+ 1 row in set (0.01 sec) ``` ```sql SHOW FULL TABLES WHERE Table_type='BASE TABLE'; ``` ```sql +---------+------------+ | Tables | Table_type | +---------+------------+ | monitor | BASE TABLE | +---------+------------+ 1 row in set (0.01 sec) ``` ## SHOW CREATE TABLE Shows the `CREATE TABLE` statement that creates the named table: ```sql SHOW CREATE TABLE [table] ``` For example: ```sql SHOW CREATE TABLE system_metrics; ``` ```sql +----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | system_metrics | CREATE TABLE IF NOT EXISTS `system_metrics` ( `host` STRING NULL, `idc` STRING NULL, `cpu_util` DOUBLE NULL, `memory_util` DOUBLE NULL, `disk_util` DOUBLE NULL, `ts` TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(), TIME INDEX (`ts`), PRIMARY KEY (`host`, `idc`) ) ENGINE=mito WITH( regions = 1 ) | +----------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ``` * `Table`: the table name. * `Create Table`: The SQL to create the table. :::note When a table has no explicit `ttl` or `compaction.*` settings, `SHOW CREATE TABLE` displays the database-level settings in the `WITH(...)` clause. This reflects the actual effective settings for the table, as these options are dynamically resolved at runtime with the following priority: table-level > database-level > defaults. ::: ## SHOW CREATE FLOW Shows the `CREATE FLOW` statement that creates the flow task. For example: ```sql public=> SHOW CREATE FLOW filter_numbers; ``` ```sql Flow | Create Flow ----------------+------------------------------------------------------- filter_numbers | CREATE OR REPLACE FLOW IF NOT EXISTS filter_numbers + | SINK TO out_num_cnt + | AS SELECT number FROM numbers_input WHERE number > 10 (1 row) ``` ## SHOW FLOWS Show all flows: ```sql public=> SHOW FLOWS; ``` ```sql Flows ---------------- filter_numbers (1 row) ``` also support `LIKE` expression: ```sql public=> show flows like "filter%"; ``` ```sql Flows ---------------- filter_numbers (1 row) ``` ## SHOW FLOW STATUS Show the runtime statistics of all flows: ```sql public=> SHOW FLOW STATUS; ``` ```sql flow_id | flow_name | start_time | last_execution_time | uptime_seconds | state_size ---------+----------------+-------------------------+-------------------------+----------------+------------ 1 | filter_numbers | 2026-09-09 02:11:38.512 | 2026-09-09 03:07:41.036 | 3363 | 1024 (1 row) ``` `LIKE` matches on the flow name: ```sql public=> SHOW FLOW STATUS LIKE 'filter%'; ``` Results are sorted by flow name. The columns are the same as [`INFORMATION_SCHEMA.FLOW_STATISTICS`](/reference/sql/information-schema/flow-statistics.md), where their meanings are described. ## SHOW CREATE VIEW To show the view's definition: ```sql SHOW CREATE VIEW cpu_monitor; ``` ``` +-------------+--------------------------------------------------------------+ | View | Create View | +-------------+--------------------------------------------------------------+ | cpu_monitor | CREATE VIEW cpu_monitor AS SELECT cpu, host, ts FROM monitor | +-------------+--------------------------------------------------------------+ ``` ## SHOW VIEWS List all views: ```sql SHOW VIEWS; ``` ```sql +----------------+ | Views | +----------------+ | cpu_monitor | | memory_monitor | +----------------+ ``` Of course, it supports `LIKE`: ```sql SHOW VIEWS LIKE 'cpu%'; ``` ```sql +-------------+ | Views | +-------------+ | cpu_monitor | +-------------+ ``` And `where`: ```sql SHOW VIEWS WHERE Views = 'memory_monitor'; ``` ```sql +----------------+ | Views | +----------------+ | memory_monitor | +----------------+ ``` ## SHOW PROCESSLIST List all currently running queries, essentially an alias for `SELECT id, catalog, query, elapsed_time FROM INFORMATION_SCHEMA.PROCESS_LIST`: ```sql SHOW PROCESSLIST; ``` Output: ``` +-----------------------+----------+------------------+-----------------+ | Id | Catalog | Query | Elapsed Time | +-----------------------+----------+------------------+-----------------+ | 192.168.50.164:4001/0 | greptime | SHOW PROCESSLIST | 00:00:00.002000 | +-----------------------+----------+------------------+-----------------+ 1 row in set (0.00 sec) ``` At the same time, you can specify the `FULL` parameter to output all columns of the `INFORMATION_SCHEMA.PROCESS_LIST` table: ```sql SHOW FULL PROCESSLIST; ``` Output: ```sql +-----------------------+----------+--------------------+------------------------+---------------------+----------------------------+-----------------+-----------------------+ | Id | Catalog | Schema | Client | Frontend | Start Time | Elapsed Time | Query | +-----------------------+----------+--------------------+------------------------+---------------------+----------------------------+-----------------+-----------------------+ | 192.168.50.164:4001/0 | greptime | information_schema | mysql[127.0.0.1:34692] | 192.168.50.164:4001 | 2025-06-30 07:17:46.423000 | 00:00:00.003000 | SHOW FULL PROCESSLIST | +-----------------------+----------+--------------------+------------------------+---------------------+----------------------------+-----------------+-----------------------+ ``` ## SHOW TRIGGERS Please refer to the [Trigger syntax](/reference/sql/trigger-syntax.md#show-triggers) documentation. ## SHOW CREATE TRIGGER Please refer to the [Trigger syntax](/reference/sql/trigger-syntax.md#show-create-trigger) documentation. ## Extensions to SHOW Statements Some extensions to `SHOW` statements accompany the implementation of [`INFORMATION_SCHEMA`](/reference/sql/information-schema/overview.md) just like MySQL, they also accept a `WHERE` clause that provides more flexibility in specifying which rows to display. GreptimeDB supports some extensions for MySQL compatibility, it's good for tools like [Navicat for MySQL](https://www.navicat.com/en/products/navicat-for-mysql) or [dbeaver](https://dbeaver.io/) to connect GreptimeDB. ```sql SHOW CHARACTER SET; ``` Output just like the `INFORMATION_SCHEMA.CHARACTER_SETS` table: ```sql +---------+---------------+-------------------+--------+ | Charset | Description | Default collation | Maxlen | +---------+---------------+-------------------+--------+ | utf8 | UTF-8 Unicode | utf8_bin | 4 | +---------+---------------+-------------------+--------+ ``` `SHOW COLLATION` for `INFORMATION_SCHEMA.COLLATIONS` table. ```sql SHOW INDEX FROM monitor; ``` To list all indexes in a table: ```sql +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ | monitor | 1 | PRIMARY | 1 | host | A | NULL | NULL | NULL | YES | greptime-inverted-index-v1 | | | YES | NULL | | monitor | 1 | TIME INDEX | 1 | ts | A | NULL | NULL | NULL | NO | greptime-inverted-index-v1 | | | YES | NULL | +---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ ``` Which is the extension of `INFORMATION_SCHEMA.TABLE_CONSTRAINTS`. List all the columns in a table: ```sql SHOW COLUMNS FROM monitor; ``` Output just like `INFORMATION_SCHEMA.COLUMNS`: ```sql +--------+--------------+------+------------+---------------------+-------+----------------------+ | Field | Type | Null | Key | Default | Extra | Greptime_type | +--------+--------------+------+------------+---------------------+-------+----------------------+ | cpu | double | Yes | | 0 | | Float64 | | host | string | Yes | PRI | NULL | | String | | memory | double | Yes | | NULL | | Float64 | | ts | timestamp(3) | No | TIME INDEX | current_timestamp() | | TimestampMillisecond | +--------+--------------+------+------------+---------------------+-------+----------------------+ ``` All these `SHOW` extensions accept `WHERE`: ```sql SHOW COLUMNS FROM monitor WHERE Field = 'cpu'; ``` ```sql +-------+--------+------+------+---------+-------+---------------+ | Field | Type | Null | Key | Default | Extra | Greptime_type | +-------+--------+------+------+---------+-------+---------------+ | cpu | double | Yes | | 0 | | Float64 | +-------+--------+------+------+---------+-------+---------------+ ``` To list all regions in a table: ```sql SHOW REGION FROM monitor; ``` ```sql +----------------+---------------+------+--------+ | Table | Region | Peer | Leader | +----------------+---------------+------+--------+ | monitor | 4398046511104 | 0 | Yes | +----------------+---------------+------+--------+ ``` It is the extension of `INFORMATION_SCHEMA.REGION_PEERS` and supports `WHERE` too. The syntax is ```sql SHOW REGION FROM [table] [IN database] [WHERE where] ``` Other `SHOW` statements: * `SHOW STATUS` and `SHOW VARIABLES` are not supported, just return empty results. * `SHOW TABLE STATUS` is the extension of `INFORMATION_SCHEMA.TABLES`. --- ## TQL The `TQL` keyword executes TQL language in SQL. The TQL is Telemetry Query Language, which is an extension for Prometheus's [PromQL](https://prometheus.io/docs/prometheus/latest/querying/basics/) in GreptimeDB. ## EVAL ### Syntax ```sql TQL [EVAL | EVALUATE] (start, end, step, [lookback]) expr [AS alias] ``` The `start`, `end` and `step` are the query parameters just like [Prometheus Query API](https://prometheus.io/docs/prometheus/latest/querying/api/): - `start`: ``: The start timestamp of the query; the range is inclusive of this value. - `end`: ``: The end timestamp of the query; the range is inclusive of this value. - `step`: ``: The query resolution step, specified as a `duration` or a floating-point number of seconds. - `lookback`: ``: The maximum lookback duration for evaluation, default is 5 minutes and optional. `expr` is the TQL (PromQL) query string. The optional `AS alias` clause allows you to specify a custom name for the value column in the result. This is useful for: - Giving meaningful names to query results - Using TQL results in SQL queries (e.g., CTEs, JOINs) - Improving readability of complex queries **Note**: Value aliasing currently supports single-field results only. ### Examples Return the per-second rate for all time series with the `http_requests_total` metric name, as measured over the last 5 minutes: ```sql TQL eval (1677057993, 1677058993, '1m') rate(prometheus_http_requests_total{job="prometheus"}[5m]); ``` will get a result just like other normal SQL queries. Using value aliasing to give a custom name to the result: ```sql TQL EVAL (0, 30, '10s') http_requests_total AS requests; ``` This will return results with the value column named `requests` instead of the default field name. Value aliasing with aggregation: ```sql TQL EVAL (0, 10, '5s') count by (k) (test) AS count_value; ``` This query counts values grouped by `k` and names the result column `count_value`. `start` and `end` can also be time expressions that evaluate to constants. For example, to query the past 3 hours: ```sql TQL EVAL (now() - interval '3' hours, now(), '1m') sum by (namespace, pod) ( increase(kube_pod_container_status_restarts_total[10m:30s]) ); ``` To query data for the past day: ```sql TQL EVAL ( date_trunc('day', now() - interval '1' day), date_trunc('day', now()), '1m' ) sum by (namespace) ( rate(http_requests_total[5m:30s]) ); ``` ### Using TQL in CTEs TQL `EVAL` can be used within Common Table Expressions (CTEs) to combine PromQL-style queries with SQL processing. For detailed examples and usage guidelines, see [Using TQL in CTEs](/user-guide/query-data/cte.md#using-tql-in-ctes). ## EXPLAIN `EXPLAIN` displays both the logical plan and execution plan for a given PromQL query. The syntax is as follows: ``` TQL EXPLAIN [VERBOSE] [FORMAT format] [(start, end, step, [lookback])] expr [AS alias]; ``` For example, to explain the PromQL `sum by (instance) (rate(node_disk_written_bytes_total[2m])) > 50`, we can use ``` TQL EXPLAIN sum by (instance) (rate(node_disk_written_bytes_total[2m])) > 50; ``` Notice that since the given query won't be actually executed, the triple `(start, end, step, [lookback])` is not necessary. But you can still provide it like in `TQL EVAL`: ``` TQL EXPLAIN (0, 100, '10s') sum by (instance) (rate(node_disk_written_bytes_total[2m])) > 50; ``` You can also use value aliasing with EXPLAIN: ``` TQL EXPLAIN (0, 10, '5s') test AS series; ``` The result should be like the following: ```txt +---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | plan_type | plan | +---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | logical_plan | Sort: node_disk_written_bytes_total.instance ASC NULLS LAST, node_disk_written_bytes_total.ts ASC NULLS LAST Filter: SUM(prom_rate(ts_range,field,ts)) > Float64(50) Aggregate: groupBy=[[node_disk_written_bytes_total.instance, node_disk_written_bytes_total.ts]], aggr=[[SUM(prom_rate(ts_range,field,ts))]] Projection: node_disk_written_bytes_total.ts, prom_rate(ts_range, field, node_disk_written_bytes_total.ts) AS prom_rate(ts_range,field,ts), node_disk_written_bytes_total.instance Filter: prom_rate(ts_range, field, node_disk_written_bytes_total.ts) IS NOT NULL Projection: node_disk_written_bytes_total.ts, node_disk_written_bytes_total.instance, field, ts_range PromRangeManipulate: req range=[0..0], interval=[300000], eval range=[120000], time index=[ts], values=["field"] PromSeriesNormalize: offset=[0], time index=[ts], filter NaN: [true] PromSeriesDivide: tags=["instance"] Sort: node_disk_written_bytes_total.instance DESC NULLS LAST, node_disk_written_bytes_total.ts DESC NULLS LAST TableScan: node_disk_written_bytes_total projection=[ts, instance, field], partial_filters=[ts >= TimestampMillisecond(-420000, None), ts <= TimestampMillisecond(300000, None)] | | physical_plan | SortPreservingMergeExec: [instance@0 ASC NULLS LAST,ts@1 ASC NULLS LAST] SortExec: expr=[instance@0 ASC NULLS LAST,ts@1 ASC NULLS LAST] CoalesceBatchesExec: target_batch_size=8192 FilterExec: SUM(prom_rate(ts_range,field,ts))@2 > 50 AggregateExec: mode=FinalPartitioned, gby=[instance@0 as instance, ts@1 as ts], aggr=[SUM(prom_rate(ts_range,field,ts))] CoalesceBatchesExec: target_batch_size=8192 RepartitionExec: partitioning=Hash([Column { name: "instance", index: 0 }, Column { name: "ts", index: 1 }], 32), input_partitions=32 AggregateExec: mode=Partial, gby=[instance@2 as instance, ts@0 as ts], aggr=[SUM(prom_rate(ts_range,field,ts))] ProjectionExec: expr=[ts@0 as ts, prom_rate(ts_range@3, field@2, ts@0) as prom_rate(ts_range,field,ts), instance@1 as instance] CoalesceBatchesExec: target_batch_size=8192 FilterExec: prom_rate(ts_range@3, field@2, ts@0) IS NOT NULL ProjectionExec: expr=[ts@0 as ts, instance@1 as instance, field@2 as field, ts_range@3 as ts_range] PromInstantManipulateExec: req range=[0..0], interval=[300000], eval range=[120000], time index=[ts] PromSeriesNormalizeExec: offset=[0], time index=[ts], filter NaN: [true] PromSeriesDivideExec: tags=["instance"] RepartitionExec: partitioning=RoundRobinBatch(32), input_partitions=1 ExecutionPlan(PlaceHolder) | +---------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ``` ## ANALYZE TQL also supports `ANALYZE` keyword to analyze the given PromQL query's execution. The syntax is as follows: ``` TQL ANALYZE [VERBOSE] [FORMAT format] (start, end, step, [lookback]) expr [AS alias]; ``` For example: ``` TQL ANALYZE (0, 10, '5s') test; ``` will get a result like ``` +-------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | plan_type | plan | +-------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Plan with Metrics | CoalescePartitionsExec, metrics=[output_rows=0, elapsed_compute=14.99µs] PromInstantManipulateExec: range=[0..10000], lookback=[300000], interval=[5000], time index=[j], metrics=[output_rows=0, elapsed_compute=1.08µs] PromSeriesNormalizeExec: offset=[0], time index=[j], filter NaN: [false], metrics=[output_rows=0, elapsed_compute=1.11µs] PromSeriesDivideExec: tags=["k"], metrics=[output_rows=0, elapsed_compute=1.3µs] RepartitionExec: partitioning=RoundRobinBatch(32), input_partitions=32, metrics=[send_time=32ns, repart_time=32ns, fetch_time=11.578016ms] RepartitionExec: partitioning=RoundRobinBatch(32), input_partitions=1, metrics=[send_time=1ns, repart_time=1ns, fetch_time=21.07µs] ExecutionPlan(PlaceHolder), metrics=[] | +-------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ``` Using `TQL ANALYZE VERBOSE` will get a verbose result of the execution. ``` TQL ANALYZE VERBOSE (0, 10, '5s') test; ``` --- ## Trigger Syntax :::tip NOTE This feature is only available in the GreptimeDB Enterprise database. ::: ## CREATE TRIGGER The syntax for creating a Trigger: ```sql CREATE TRIGGER [IF NOT EXISTS] ON () EVERY [LABELS (=, ...)] [ANNOTATIONS (=, ...)] [FOR ] [KEEP FIRING FOR ] NOTIFY ( WEBHOOK URL '' [WITH (=, ...)], WEBHOOK URL '' [WITH (=, ...)] ); ``` - Trigger name: the unique identifier of the Trigger at the catalog level. - IF NOT EXISTS: prevent errors that would occur if the Trigger being created. ### On clause #### Query expression The SQL query specified in the `ON` clause is executed periodically. Its evaluation result may produce one or more alert instances, depending on the returned rows. The Trigger extracts `labels` and `annotations` from the query result and attaches them to each alert instance. These values are combined with the static key-value pairs specified in the `LABELS` and `ANNOTATIONS` clauses. The extraction rules are as follows: - Extract columns whose name or alias starts with `label_` to `LABELS`. The key of labels is the column name or alias without the `label_` prefix. - Extract the other columns to `ANNOTATIONS`. The key of annotations is the column name. It is worth noting that each alert instance is uniquely identified by its label set. Multiple rows with the same labels will only create a single alert instance. For example, the query expression is as follows: ```sql SELECT collect as label_collector, host as label_host, val FROM host_load1 WHERE val > 10 and ts >= now() - '1 minutes'::INTERVAL ``` Assume the query result is not empty and looks like this: | label_collector | label_host | val | |------------------|------------|-----| | collector1 | host1 | 12 | | collector2 | host2 | 15 | This will generate two alert instances. The first alert instance will have the following labels and annotations: - Labels: - collector: collector1 - host: host1 - the labels defined in the `LABELS` clause - Annotations: - val: 12 - the annotations defined in the `ANNOTATIONS` clause The second alert instance will have the following labels and annotations: - Labels: - collector: collector2 - host: host2 - the labels defined in the `LABELS` clause - Annotations: - val: 15 - the annotations defined in the `ANNOTATIONS` clause #### Interval expression It indicates how often the query is executed. e.g., `'5 minute'::INTERVAL`, `'1 hour'::INTERVAL` etc. - `Years` and `months` are **prohibited** in INTERVAL expressions. Because the duration of a month or year is variable and depends on the specific month or year, it is not suitable for defining fixed intervals. - The minimum interval is 1 second. Any interval specified less than 1 second will be automatically rounded up to 1 second. For more details about how to write INTERVAL time, see [interval-type](/reference/sql/data-types.md#interval-type). ### FOR clause The `FOR` clause controls how long an alert must remain active before it fires. Its behavior is similar to the `for` option in Prometheus Alerting Rules. When an alert instance appears in the evaluation results for the first time, it enters the `Pending` state, during which no notification is sent. If the alert instance remains active throughout every evaluation within the duration specified by `FOR`, its state transitions from `Pending` to `Firing`, and a notification is sent immediately. If the `FOR` clause is not specified, the alert will not enter the `Pending` state. Instead, an alert instance transitions to the `Firing` state immediately upon its first appearance in the evaluation results, and a notification is sent immediately. ### KEEP FIRING FOR clause The `KEEP FIRING FOR` clause controls how long an alert instance should remain in the `Firing` state after it first enters that state. Its behavior is similar to the `keep_firing_for` option in Prometheus Alerting Rules. Once an alert instance enters the `Firing` state, it will remain firing for at least the duration specified by `KEEP FIRING FOR`, even if it no longer appears in subsequent evaluation results. After the `KEEP FIRING FOR` duration has passed, if the alert instance does not appear in the next evaluation, it will be marked as resolved and will no longer remain in the `Firing` state. If the `KEEP FIRING FOR` clause is not specified, an alert instance will be marked as resolved in the first evaluation where it no longer appears in the query results after entering the `Firing` state. ### Labels and Annotations clauses The LABELS and ANNOTATIONS clauses allow you to attach static key-value pairs to the notification messages sent by Trigger. These can be used to provide additional context or metadata about the Trigger. - LABELS: serve as labels for Alertmanager routing, grouping, and inhibition. - ANNOTATIONS: typically for human-readable descriptions. ### Notify clause The NOTIFY clause allows you to specify one or more notification channels. Currently, GreptimeDB supports the following notification channel: #### Webhook The webhook channel will send HTTP requests to a specified URL when the Trigger fires. The payload of the http request is compatible with [Prometheus Alertmanager](https://prometheus.io/docs/alerting/latest/alertmanager/), which means you can use GreptimeDB's Trigger with Prometheus Alertmanager without any extra glue code. The optional `WITH` clause allows you to specify additional parameters: - timeout: The timeout for the HTTP request, e.g., `timeout='1m'`. ## SHOW TRIGGERS Show all triggers: ```sql SHOW TRIGGERS; ``` Show triggers by `like` pattern: ```sql SHOW TRIGGERS LIKE ''; ``` For example: ```sql SHOW TRIGGERS LIKE 'load%'; ``` Show triggers by `where` condition: ```sql SHOW TRIGGERS WHERE ; ``` For example: ```sql SHOW TRIGGERS WHERE name = 'load1_monitor'; ``` ## SHOW CREATE TRIGGER To show the Trigger's definition: ```sql SHOW CREATE TRIGGER ; ``` For example: ```sql SHOW CREATE TRIGGER load1_monitor; ``` ## DROP TRIGGER To delete a trigger, use the following `DROP TRIGGER` clause: ```sql DROP TRIGGER [IF EXISTS] ; ``` For example: ```sql DROP TRIGGER IF EXISTS load1_monitor; ``` ## Example Please refer to the [Trigger](/enterprise/trigger.md) documentation in the enterprise user guide for examples. --- ## TRUNCATE `TRUNCATE TABLE` removes all rows from a table while retaining its definition. It is much more efficient than `DELETE FROM table`. ```sql TRUNCATE TABLE monitor; ``` ```sql Query OK, 0 rows affected (0.02 sec) ``` --- ## WHERE `WHERE` clause allows to filter the data by specifying conditions. ## Syntax ```sql SELECT * FROM table_name WHERE condition; ``` If there is a `WHERE` clause, it must contain an expression with the Boolean type. This is usually an expression with comparison and logical operators. Rows where this expression evaluates to false are excluded from further transformations or result. ## Examples ### Logical operators Supports `AND`, `OR` as logical operators and can assemble conditions using brackets (). ```sql SELECT * FROM system_metrics WHERE idc = 'idc0' AND (host = 'host1' OR host = 'host2'); ``` ### Numeric Supports `=`, `!=`, `>`, `>=`, `<`, `<=` as comparison operators. ```sql SELECT * FROM system_metrics WHERE cpu_util = 20.0; SELECT * FROM system_metrics WHERE cpu_util != 20.0; SELECT * FROM system_metrics WHERE cpu_util > 20.0; SELECT * FROM system_metrics WHERE cpu_util >= 20.0; SELECT * FROM system_metrics WHERE cpu_util < 20.0; SELECT * FROM system_metrics WHERE cpu_util <= 20.0; ``` ### List search Evaluates match or mismatch against a list of elements. ### List match ```sql SELECT * FROM system_metrics WHERE idc IN ('idc_a', 'idc_b'); ``` ### List mismatch ```sql SELECT * FROM system_metrics WHERE idc NOT IN ('idc_a', 'idc_b'); ``` ### String For string columns, we can use the `LIKE` operator to search for a specified pattern in a column. There are two wildcards often used in conjunction with the LIKE operator: * The percent sign `%` represents zero, one, or multiple characters * The underscore sign `_` represents a single character Select all records that the `host` column starts with the letter "a": ```sql SELECT * FROM system_metrics WHERE host LIKE 'a%'; ``` Selects all records from the `go_info` table where the instance column matches the pattern `'localhost:____'`, meaning `'localhost:'` followed by exactly four characters. ```sql SELECT * FROM go_info WHERE instance LIKE 'localhost:____'; ``` For searching terms in logs, please read [Fulltext Search](/user-guide/logs/fulltext-search.md). --- ## WITH Use `WITH` to specify a Common Table Expression. ## What is a Common Table Expression (CTE)? A Common Table Expression (CTE) is a temporary result set defined by a `WITH` clause. The query that follows the clause can reference the CTE multiple times. ## Basic syntax of CTE CTEs are typically defined using the `WITH` keyword. The basic syntax is as follows: ```sql WITH cte_name [(column1, column2, ...)] AS ( QUERY ) SELECT ... FROM cte_name; ``` ## Examples ### Non-recursive CTE ```sql WITH cte AS (SELECT 0 AS number UNION ALL SELECT 1) SELECT * FROM cte t1, cte t2; ``` ```sql +--------+--------+ | number | number | +--------+--------+ | 0 | 0 | | 0 | 1 | | 1 | 0 | | 1 | 1 | +--------+--------+ ``` If a parenthesized list of names follows the CTE name, those names are the column names: ```sql WITH cte (col1, col2) AS ( SELECT 1, 2 UNION ALL SELECT 3, 4 ) SELECT col1, col2 FROM cte; ``` The number of names in the list must be the same as the number of columns in the result set. ```sql +------+------+ | col1 | col2 | +------+------+ | 1 | 2 | | 3 | 4 | +------+------+ ``` Join two CTEs: ```sql WITH cte1 AS (SELECT 0 AS a UNION ALL SELECT 1), cte2 AS (SELECT 0 AS b UNION ALL SELECT 1) SELECT * FROM cte1 JOIN cte2 ON cte1.a = cte2.b; ``` ```sql +------+------+ | a | b | +------+------+ | 1 | 1 | | 0 | 0 | +------+------+ ``` ### Recursive CTE A recursive CTE is declared with `WITH RECURSIVE`. It takes an anchor term, then a recursive term that references the CTE by name: ```sql WITH RECURSIVE counter(n) AS ( SELECT 1 AS n UNION ALL SELECT n + 1 FROM counter WHERE n < 5 ) SELECT n FROM counter; ``` ```sql +---+ | n | +---+ | 1 | | 2 | | 3 | | 4 | | 5 | +---+ ``` The recursion stops when an iteration produces no rows. There is no iteration limit and no cycle detection, so a recursive term that keeps producing rows runs until the query is cancelled. --- ## SQL Tools GreptimeDB uses SQL as its main query language and supports many popular SQL tools. This document guides you on how to use SQL tools with GreptimeDB. ## Language drivers It is recommended to use mature SQL drivers to query data. ### Recommended libraries Java Database Connectivity (JDBC) is the JavaSoft specification of a standard application programming interface (API) that allows Java programs to access database management systems. Many databases, such as MySQL or PostgreSQL, have implemented their own drivers based on the JDBC API. Since GreptimeDB supports [multiple protocols](/user-guide/protocols/overview.md), we use MySQL as an example to demonstrate how to use JDBC. If you want to use other protocols, just replace the MySQL driver with the corresponding driver. It is recommended to use the [GORM](https://gorm.io/) library, which is popular and developer-friendly. ### Installation If you are using [Maven](https://maven.apache.org/), add the following to your `pom.xml` dependencies list: ```xml mysql mysql-connector-java 8.0.33 ``` Use the following command to install the GORM library: ```shell go get -u gorm.io/gorm ``` Then install the MySQL driver: ```shell go get -u gorm.io/driver/mysql ``` Import the libraries in your code: ```go "gorm.io/gorm" "gorm.io/driver/mysql" ) ``` ### Connect to database The following use MySQL as an example to demonstrate how to connect to GreptimeDB. ```java public static Connection getConnection() throws IOException, ClassNotFoundException, SQLException { Properties prop = new Properties(); prop.load(QueryJDBC.class.getResourceAsStream("/db-connection.properties")); String dbName = (String) prop.get("db.database-driver"); String dbConnUrl = (String) prop.get("db.url"); String dbUserName = (String) prop.get("db.username"); String dbPassword = (String) prop.get("db.password"); Class.forName(dbName); Connection dbConn = DriverManager.getConnection(dbConnUrl, dbUserName, dbPassword); return Objects.requireNonNull(dbConn, "Failed to make connection!"); } ``` You need a properties file to store the DB connection information. Place it in the resources directory and name it `db-connection.properties`. The file content is as follows: ```txt # DataSource db.database-driver=com.mysql.cj.jdbc.Driver db.url=jdbc:mysql://localhost:4002/public db.username= db.password= ``` Or you can get the file from [here](https://github.com/GreptimeTeam/greptimedb-ingester-java/blob/main/ingester-example/src/main/resources/db-connection.properties). ```go type Mysql struct { Host string Port string User string Password string Database string DB *gorm.DB } m := &Mysql{ Host: "127.0.0.1", Port: "4002", // default port for MySQL User: "username", Password: "password", Database: "public", } dsn := fmt.Sprintf("tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", m.Host, m.Port, m.Database) dsn = fmt.Sprintf("%s:%s@%s", m.User, m.Password, dsn) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { // error handling } m.DB = db ``` #### Time zone Set the JDBC time zone by setting URL parameters: ```txt jdbc:mysql://127.0.0.1:4002?connectionTimeZone=Asia/Shanghai&forceConnectionTimeZoneToSession=true ``` * `connectionTimeZone={LOCAL|SERVER|user-defined-time-zone}` specifies the connection time zone. * `forceConnectionTimeZoneToSession=true` sets the session `time_zone` variable to the value specified in `connectionTimeZone`. Set the time zone in the DSN. For example, set the time zone to `Asia/Shanghai`: ```go dsn := fmt.Sprintf("tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local&time_zone=%27Asia%2FShanghai%27", m.Host, m.Port, m.Database) ``` For more information, see the [MySQL Driver Documentation](https://github.com/go-sql-driver/mysql?tab=readme-ov-file#system-variables). ### Raw SQL It is recommended to use raw SQL to experience the full features of GreptimeDB. The following example shows how to use raw SQL to query data. ```java try (Connection conn = getConnection()) { Statement statement = conn.createStatement(); // DESC table; ResultSet rs = statement.executeQuery("DESC cpu_metric"); LOG.info("Column | Type | Key | Null | Default | Semantic Type "); while (rs.next()) { LOG.info("{} | {} | {} | {} | {} | {}", rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5), rs.getString(6)); } // SELECT COUNT(*) FROM cpu_metric; rs = statement.executeQuery("SELECT COUNT(*) FROM cpu_metric"); while (rs.next()) { LOG.info("Count: {}", rs.getInt(1)); } // SELECT * FROM cpu_metric ORDER BY ts DESC LIMIT 5; rs = statement.executeQuery("SELECT * FROM cpu_metric ORDER BY ts DESC LIMIT 5"); LOG.info("host | ts | cpu_user | cpu_sys"); while (rs.next()) { LOG.info("{} | {} | {} | {}", rs.getString("host"), rs.getTimestamp("ts"), rs.getDouble("cpu_user"), rs.getDouble("cpu_sys")); } } ``` For the complete code of the demo, please refer to [here](https://github.com/GreptimeTeam/greptimedb-ingester-java/blob/main/ingester-example/src/main/java/io/greptime/QueryJDBC.java). The following code declares a GORM object model: ```go type CpuMetric struct { Host string `gorm:"column:host;primaryKey"` Ts time.Time `gorm:"column:ts;primaryKey"` CpuUser float64 `gorm:"column:cpu_user"` CpuSys float64 `gorm:"column:cpu_sys"` } ``` If you are using the [High-level API](/user-guide/ingest-data/for-iot/grpc-sdks/go.md#high-level-api) to insert data, you can declare the model with both GORM and GreptimeDB tags. ```go type CpuMetric struct { Host string `gorm:"column:host;primaryKey" greptime:"tag;column:host;type:string"` Ts time.Time `gorm:"column:ts;primaryKey" greptime:"timestamp;column:ts;type:timestamp;precision:millisecond"` CpuUser float64 `gorm:"column:cpu_user" greptime:"field;column:cpu_user;type:float64"` CpuSys float64 `gorm:"column:cpu_sys" greptime:"field;column:cpu_sys;type:float64"` } ``` Declare the table name as follows: ```go func (CpuMetric) TableName() string { return "cpu_metric" } ``` Use raw SQL to query data: ```go var cpuMetric CpuMetric db.Raw("SELECT * FROM cpu_metric LIMIT 10").Scan(&result) ``` ### Query library reference For more information about how to use the query library, please see the documentation of the corresponding library: - [JDBC Online Tutorials](https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html) - [GORM](https://gorm.io/docs/index.html) ## Command line tools ### MySQL You can use the `mysql` command line tool to connect to the GreptimeDB. Please refer to the [MySQL protocol](/user-guide/protocols/mysql.md) document for connection information. After you connect to the server, you can use all [GreptimeDB SQL commands](/reference/sql/overview.md) to interact with the database. ### PostgreSQL You can use the `psql` command line tool to connect to the GreptimeDB. Please refer to the [PostgreSQL protocol](/user-guide/protocols/postgresql.md) document for connection information. After you connect to the server, you can use all [GreptimeDB SQL commands](/reference/sql/overview.md) to interact with the database. ## GreptimeDB Dashboard You can run SQL and visualize data in the [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md). ## GUI tools ### DBeaver Please refer to the [DBeaver Integration Guide](/user-guide/integrations/dbeaver.md). ## HTTP API You can POST SQL queries to the GreptimeDB HTTP API to query data. Please refer to the [HTTP API](/user-guide/protocols/http.md) document for more information. --- ## Telemetry GreptimeDB enables anonymous telemetry by default. The telemetry payload contains the installation and runtime fields listed below. It does not contain database names, table names, or query text. You can disable telemetry in the GreptimeDB configuration. ## What data will be collected? The fields in the telemetry payload may change in a future release. Such changes are documented in the release notes. When telemetry is enabled, GreptimeDB sends a report when the telemetry task starts and every 30 minutes afterward. Each report contains: - GreptimeDB version - GreptimeDB build git hash - The operating system of the machine on which GreptimeDB is running(Linux, macOS, etc.) - Architecture of the machine on which GreptimeDB is running(x86_64, arm64, etc.) - Mode in which GreptimeDB is running(standalone, distributed) - A randomly generated installation ID - The number of datanodes in the GreptimeDB cluster - System uptime, not exact figures, only time ranges like `hours`, `weeks` with no numbers Sample telemetry data: ```json { "os": "linux", "version": "0.15.1", "arch": "aarch64", "mode": "standalone", "git_commit": "00d759e828f5e148ec18141904e20cb1cb7577b0", "nodes": 1, "uuid": "43717682-baa8-41e0-b126-67b797b66606", "uptime": "hours" } ``` ## How to disable telemetry? Telemetry has been enabled by default since GreptimeDB v0.4.0. ### Standalone mode Set `enable_telemetry` in the standalone config file to `false`: ```toml # Whether to enable greptimedb telemetry, true by default. enable_telemetry = false ``` Or configure it by the environment variable `GREPTIMEDB_STANDALONE__ENABLE_TELEMETRY=false` on startup. ### Distributed mode Set `enable_telemetry` in the metasrv config file to `false`: ```toml # metasrv config file # Whether to enable greptimedb telemetry, true by default. enable_telemetry = false ``` Or set the environment variable `GREPTIMEDB_METASRV__ENABLE_TELEMETRY=false` on startup. --- ## Time Durations GreptimeDB utilizes time durations to represent time spans in various contexts, including SQL queries, configuration files, and API requests. For more information on using time durations, please refer to: - The TTL options and the `time_window` parameter of the TWCS compaction strategy in the [ALTER](/reference/sql/alter.md) statement. - The TTL options in the [CREATE](/reference/sql/create.md) statement. A time duration is expressed as a string composed of concatenated time spans, each represented by a sequence of decimal numbers followed by a unit suffix. These suffixes are case-insensitive and support both singular and plural forms. For example, `1hour 12min 5s`. Each time span consists of an integer and a suffix. The supported suffixes are: - `nsec`, `ns`: nanoseconds - `usec`, `us`: microseconds - `msec`, `ms`: milliseconds - `seconds`, `second`, `sec`, `s` - `minutes`, `minute`, `min`, `m` - `hours`, `hour`, `hr`, `h` - `days`, `day`, `d` - `weeks`, `week`, `w` - `months`, `month`, `M`: defined as 30.44 days - `years`, `year`, `y`: defined as 365.25 days Appending a decimal integer with one of the above units represents the equivalent number of seconds as a bare float literal. Examples: - `1s`: Equivalent to 1 second - `2m`: Equivalent to 120 seconds - `1ms`: Equivalent to 0.001 seconds - `2h`: Equivalent to 7200 seconds The following examples are invalid: - `0xABm`: Hexadecimal numbers are not supported - `1.5h`: Floating point numbers are not supported - `+Infd`: `±Inf` or `NaN` values are not supported The following are some valid time duration examples: - `1h`: one hour - `1h30m`, `1h 30m`: one hour and thirty minutes - `1h30m10s`, `1h 30m 10s`: one hour, thirty minutes, and ten seconds --- ## GreptimeDB FAQ # Frequently Asked Questions :::tip Looking for introductory information? - What is GreptimeDB / Use cases → [Why GreptimeDB](/user-guide/concepts/why-greptimedb.md) - Performance benchmarks → [Key Features](/user-guide/concepts/features-that-you-concern.md#how-is-greptimedbs-performance-compared-to-other-solutions) - Metrics, logs, and traces → [Key Features](/user-guide/concepts/features-that-you-concern.md#how-does-greptimedb-handle-metrics-logs-and-traces) - High cardinality → [Key Features](/user-guide/concepts/features-that-you-concern.md#how-does-greptimedb-address-the-high-cardinality-issue) - Data model → [Data Model](/user-guide/concepts/data-model.md) ::: ## General ### Where can I find ready-to-run demos? Check out the [demo-scene](https://github.com/GreptimeTeam/demo-scene) repository. It contains end-to-end examples covering common use cases (metrics, logs, IoT, etc.) that you can run locally with Docker Compose. ### How is GreptimeDB's performance compared to other solutions? GreptimeDB is designed for high-throughput write, low-latency query, and cost-efficient storage across metrics, logs, and traces workloads. Benchmark results are available in the following reports: - [GreptimeDB vs. InfluxDB](https://greptime.com/blogs/2024-08-07-performance-benchmark) - [GreptimeDB vs. TimescaleDB](https://greptime.com/blogs/2025-12-09-greptimedb-vs-timescaledb-benchmark) - [GreptimeDB vs. Grafana Mimir](https://greptime.com/blogs/2024-08-02-datanode-benchmark) - [GreptimeDB vs. ClickHouse vs. Elasticsearch (Log Benchmark)](https://greptime.com/blogs/2025-03-10-log-benchmark-greptimedb) - [GreptimeDB vs. SQLite](https://greptime.com/blogs/2024-08-30-sqlite) - [GreptimeDB vs. Loki](https://greptime.com/blogs/2025-08-07-beyond-loki-greptimedb-log-scenario-performance-report) - [JSONBench #1 (1 Billion Cold Run)](https://greptime.com/blogs/2025-03-18-jsonbench-greptimedb-performance) For a summary of how GreptimeDB compares architecturally, see the [comparison table](/user-guide/concepts/why-greptimedb.md#how-greptimedb-compares). ### What are GreptimeDB's design trade-offs? GreptimeDB is optimized for observability and time-series workloads, which means it makes different trade-offs than a general-purpose OLTP database: - **No ACID transactions**: Prioritizes high-throughput writes over transactional consistency. - **Delete is supported but not optimized for high-frequency use**: GreptimeDB supports [row deletion](/user-guide/manage-data/overview.md#delete-data) and [TTL-based expiration](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies), but it is not designed for workloads that require frequent, fine-grained deletes — as is typical for append-heavy observability data. - **Joins are functional but currently not the primary focus**: GreptimeDB supports SQL joins, but the query engine is optimized for time-series filter-aggregate patterns rather than complex multi-way relational joins. Simple joins (e.g., enrichment lookups, correlating metrics with logs) work well. - **Time-series focused**: Optimized for IoT, metrics, logs, and traces rather than general OLTP. ### What's the difference between GreptimeDB and InfluxDB? Key differences: - **Open Source**: GreptimeDB's entire distributed system is fully open source. - **Architecture**: Region-based sharding design optimized for observability workloads. - **Query Languages**: SQL + PromQL vs. InfluxQL + SQL. - **Unified Model**: Native support for metrics, logs, and traces in one system. - **Storage**: Pluggable engines with dedicated optimizations. - **Cloud-Native**: Built for Kubernetes with disaggregated compute/storage (see [Kubernetes Deployment Guide](/user-guide/deployments-administration/deploy-on-kubernetes/overview.md)). For a detailed comparison, see [GreptimeDB vs InfluxDB](https://greptime.com/compare/influxdb). Additional product comparisons (vs. ClickHouse, Loki, etc.) are available in the Compare menu on the [website](https://greptime.com). ## Data Model & Schema ### What's the difference between Tag and Field columns? GreptimeDB uses three semantic column types: **Tag**, **Timestamp**, and **Field**. - **Tag** columns identify a time-series. Rows with the same tag values belong to the same series. Tags are indexed by default for fast filtering. In SQL, tag columns are declared via `PRIMARY KEY`. - **Field** columns contain the measured values (numbers, strings, etc.). Fields are not indexed by default, but you can add [indexes](/user-guide/manage-data/data-index.md) as needed. - **Timestamp** is the time index — every table must have exactly one. For example, in a `system_metrics` table, `host` and `idc` are tags, `cpu_util` and `memory_util` are fields, and `ts` is the timestamp. For full details and examples, see [Data Model](/user-guide/concepts/data-model.md) and the [schema design guide](/user-guide/deployments-administration/performance-tuning/design-table.md). ### Does GreptimeDB support schemaless data ingestion? Yes. When writing via gRPC, InfluxDB Line Protocol, OpenTSDB, Prometheus Remote Write, OpenTelemetry, Loki, or Elasticsearch-compatible APIs, GreptimeDB creates tables and columns automatically on first write. No manual schema definition is needed. For details, see [Automatic Schema Generation](/user-guide/ingest-data/overview.md#automatic-schema-generation). ### How do I set default table options (TTL, append mode, etc.) for auto-created tables? There are three ways to control table options such as `ttl`, `append_mode`, `merge_mode`, `skip_wal`, and `sst_format` for tables created via [automatic schema generation](/user-guide/ingest-data/overview.md#automatic-schema-generation): 1. **Set options at ingestion time via HTTP header**: Use the `x-greptime-hints` header to pass table options when writing data. For example, `x-greptime-hints: ttl=7d, append_mode=true`. These options apply when the table is auto-created. See [HTTP Hints](/user-guide/protocols/http.md#hints) for all supported hints. 2. **Modify options after table creation**: Some options can be changed on an existing table via `ALTER TABLE`. For example, `ttl`, `append_mode`, `compaction.*`, and `sst_format` are supported: ```sql ALTER TABLE my_table SET 'ttl' = '7d'; ALTER TABLE my_table SET 'append_mode' = 'true'; ``` Note that `merge_mode` and `skip_wal` cannot be altered after creation — they must be set at table creation time. See [ALTER TABLE](/reference/sql/alter.md#alter-table-options) for all supported options and constraints. 3. **Set database-level defaults**: Create or alter the database with default options. New auto-created tables will inherit these values: ```sql CREATE DATABASE my_db WITH (ttl = '7d', append_mode = 'true'); -- or ALTER DATABASE my_db SET 'ttl' = '7d'; ``` Note that `ttl` and `compaction.*` options have ongoing effect — tables without their own settings continuously inherit the database value. Other options (`append_mode`, `merge_mode`, `skip_wal`, `sst_format`) only serve as defaults for newly created tables. See [CREATE DATABASE](/reference/sql/create.md#create-database) for all available options. ### How do I customize the default column names for InfluxDB / Prometheus protocols? When writing via certain protocols, GreptimeDB generates default column names with a `greptime_` prefix. The timestamp column is named `greptime_timestamp` for all schemaless protocols. The value column `greptime_value` is used by single-value protocols such as Prometheus Remote Write and OpenTelemetry Metrics, where each time series carries only one numeric value. Multi-field protocols like InfluxDB Line Protocol use the field names from the input data directly — only the timestamp column gets the default prefix. To change the prefix, set the `default_column_prefix` option in your `standalone.toml` or `frontend.toml`: ```toml # Remove the "greptime_" prefix — columns become "value" and "timestamp" default_column_prefix = "" # Or use a custom prefix — columns become "my_value" and "my_timestamp" # default_column_prefix = "my" ``` If unset, the default `greptime_` prefix is used. This is a top-level configuration option and requires a restart to take effect. ### Can I change a column's type after table creation? Yes. Use `ALTER TABLE ... MODIFY COLUMN` to change a field column's data type: ```sql ALTER TABLE monitor MODIFY COLUMN load_15 STRING; ``` The column must be a field (not a tag or time index) and must be nullable, so that values that cannot be cast return `NULL` instead of failing. For the full `ALTER TABLE` syntax, see the [SQL reference](/reference/sql/alter.md). ### How does GreptimeDB handle late-arriving or out-of-order data? GreptimeDB accepts writes with any timestamp — there is no ingestion-time window or ordering requirement. Late-arriving and out-of-order data is written normally and becomes queryable immediately. The storage engine's [compaction](/user-guide/deployments-administration/manage-data/compaction.md) process merges and sorts data in the background. For append-only tables (commonly used for logs), rows are never deduplicated, so late-arriving data simply adds new rows. For tables with a primary key, rows with the same tag + timestamp combination follow the [update semantics](/user-guide/manage-data/overview.md#update-data). ## Integration & Migration ### What protocols, tools, and SDKs does GreptimeDB support? **Write protocols**: [OpenTelemetry (OTLP)](/user-guide/ingest-data/for-observability/opentelemetry.md), [Prometheus Remote Write](/user-guide/ingest-data/for-observability/prometheus.md), [InfluxDB Line Protocol](/user-guide/ingest-data/for-iot/influxdb-line-protocol.md), [Loki](/user-guide/ingest-data/for-observability/loki.md), [Elasticsearch](/user-guide/ingest-data/for-observability/elasticsearch.md), [MySQL](/user-guide/protocols/mysql.md), [PostgreSQL](/user-guide/protocols/postgresql.md), [gRPC](/user-guide/protocols/grpc.md) — see [Protocols Overview](/user-guide/protocols/overview.md). **Query languages**: [SQL](/user-guide/query-data/sql.md), [PromQL](/user-guide/query-data/promql.md). **Visualization**: [Grafana](/user-guide/integrations/grafana.md) (official plugin + MySQL/PostgreSQL data sources), and any tool that speaks MySQL or PostgreSQL wire protocol. **Data pipeline**: Vector, Fluent Bit, Telegraf, Kafka — see [Integrations Overview](/user-guide/integrations/overview.md). **SDKs**: - [Go](https://github.com/GreptimeTeam/greptimedb-ingester-go) - [Java](https://github.com/GreptimeTeam/greptimedb-ingester-java) - [Rust](https://github.com/GreptimeTeam/greptimedb-ingester-rust) - [Erlang](https://github.com/GreptimeTeam/greptimedb-ingester-erl) - [.NET](https://github.com/GreptimeTeam/greptimedb-ingester-dotnet) - [TypeScript](https://github.com/GreptimeTeam/greptimedb-ingester-ts) - For other languages (Python, Ruby, etc.): use any OpenTelemetry SDK, InfluxDB client library, or MySQL/PostgreSQL driver — GreptimeDB is compatible with all of them. ### How do I choose the right ingestion protocol? GreptimeDB supports multiple ingestion protocols with very different throughput characteristics. The following results are from a local benchmark at 1 million time series (batch size 1,000) — **focus on the relative ratios rather than absolute numbers**, as actual throughput varies by hardware and workload: | Protocol | Relative throughput | | --- | --- | | gRPC Bulk (Arrow Flight) | Highest (~37x SQL) | | gRPC Stream | ~21x SQL | | gRPC SDK (Unary) | ~16x SQL | | InfluxDB Line Protocol | ~12x SQL | | OTLP Logs | ~8.5x SQL | | MySQL / PostgreSQL INSERT | Baseline | **How to choose:** - **General workloads**: gRPC SDK — best balance of simplicity and performance, with schemaless support. - **Bulk operations** (migrations, backfills): gRPC Bulk — maximum throughput, requires pre-created tables. - **Continuous streams** (IoT, monitoring collectors): gRPC Stream — sustained high throughput over persistent connections. - **Ecosystem integration**: InfluxDB Line Protocol (Telegraf-compatible) or OTLP (OpenTelemetry-compatible) — good throughput with broad tool support. - **Development / debugging**: SQL protocols (MySQL / PostgreSQL) — lower throughput but easier to inspect and debug. For the full benchmark details and methodology, see the [Ingestion Protocol Benchmark](https://greptime.com/blogs/2026-03-24-ingestion-protocol-benchmark) blog post. ### How do I connect Grafana to GreptimeDB? GreptimeDB works with Grafana through three data source options: - **[GreptimeDB plugin](/user-guide/integrations/grafana.md#greptimedb-data-source-plugin)**: Official plugin with full SQL and PromQL support. - **[Prometheus data source](/user-guide/integrations/grafana.md#prometheus-data-source)**: Use GreptimeDB's Prometheus-compatible endpoint for PromQL dashboards. - **[MySQL data source](/user-guide/integrations/grafana.md#mysql-data-source)**: Use the built-in MySQL data source with GreptimeDB's MySQL protocol endpoint. See [Grafana Integration](/user-guide/integrations/grafana.md) for setup instructions. ### How can I migrate from other databases? GreptimeDB provides migration guides for: - **From InfluxDB**: Line protocol and data migration - **From Prometheus**: Remote write and historical data migration - **From ClickHouse**: Table schema and data migration - **From MySQL/PostgreSQL**: SQL-based migration For detailed instructions, see [Migration Overview](/user-guide/migrate-to-greptimedb/overview.md). ## Deployment & Operations ### What are the deployment options? **Cluster deployment** (production): - Minimum 3 nodes for high availability - Three components: metasrv, frontend, and datanode - Components can be co-located or separated depending on scale - See [Capacity Planning Guide](/user-guide/deployments-administration/capacity-plan.md) **Standalone** (development / edge / IoT): - Single binary, all components in one process - Runs on Linux, macOS, Android ARM64, Raspberry Pi - See [Installation Guide](/getting-started/installation/overview.md) **Storage backends**: S3, GCS, Azure Blob (recommended for production); local disk (development, testing, or small-scale). Metadata: RDS or etcd (cluster), local storage (standalone). For a full overview, see [Deployments & Administration](/user-guide/deployments-administration/overview.md). ### Which metadata storage backend should I use for metasrv? GreptimeDB supports etcd, MySQL, and PostgreSQL as metadata storage backends for the metasrv component. For production deployments, **PostgreSQL or MySQL (including cloud RDS services) is generally recommended** — most teams already have operational experience, monitoring, backup, and disaster recovery strategies for relational databases. That said, **etcd remains fully supported and actively maintained**. It is not deprecated. If your team has strong etcd expertise and operational tooling, it is a perfectly valid choice. The decision ultimately comes down to your team's skill set and existing infrastructure. See [Metadata Storage Configuration](/user-guide/deployments-administration/manage-metadata/configuration.md) for setup instructions for each backend. ### What should I do if etcd reports `NOSPACE`? `NOSPACE` means etcd has reached its backend storage quota. A common cause is retaining too many historical versions because automatic compaction is disabled or its retention is too long. Frequent `ALTER TABLE` requests repeatedly update table metadata (table info), accumulating historical values even when the number of tables stays unchanged. GreptimeDB does not need these historical versions to look back at table metadata. For the Bitnami etcd Helm chart, use revision-based auto compaction: ```yaml autoCompactionMode: "revision" autoCompactionRetention: "100" ``` Every 5 minutes, etcd compacts up to `current revision - 100`. See [Manage etcd](/user-guide/deployments-administration/manage-metadata/manage-etcd.md#install) for the full configuration. If `NOSPACE` has already occurred, compact old revisions, [defragment the database](https://etcd.io/docs/v3.6/op-guide/maintenance/#defragmentation) to reclaim disk space, and disarm the alarm after usage falls below the quota. If current data alone approaches the quota, review capacity as well. For production deployments, prefer managed MySQL or PostgreSQL (RDS) for metadata storage. This avoids etcd-specific `NOSPACE` errors caused by retaining too many historical versions and reduces maintenance work such as configuring auto compaction and running defragmentation. See [Metadata Storage Configuration](/user-guide/deployments-administration/manage-metadata/configuration.md) for setup instructions. ### How do I manage GreptimeDB? GreptimeDB uses **standard SQL as its management interface**. You can [create tables](/user-guide/deployments-administration/manage-data/basic-table-operations.md), [alter schemas](/reference/sql/alter.md), set [TTL policies](/user-guide/manage-data/overview.md#manage-data-retention-with-ttl-policies), and configure [indexes](/user-guide/manage-data/data-index.md) — all through SQL. No config files to write, no proprietary APIs to call. For configuration beyond SQL (e.g., node-level settings), see the [Configuration Guide](/user-guide/deployments-administration/configuration.md). ### How do I upgrade GreptimeDB? See the [Upgrade Guide](/user-guide/deployments-administration/upgrade.md) for version-specific instructions, compatibility notes, and breaking changes. ### How do I back up and restore data? GreptimeDB provides export and import tools for full database backup and restoration, including schema-only operations and S3 export support. See [Data Export & Import](/user-guide/deployments-administration/disaster-recovery/back-up-&-restore-data.md). ### Can I migrate data between standalone and cluster mode? Yes. Use [`COPY TO`](/reference/sql/copy.md) to export tables from standalone mode, then [`COPY FROM`](/reference/sql/copy.md) to import them into a cluster (or vice versa). Data is exported in Parquet format and can be staged locally or in object storage (S3, GCS). ### My WAL directory is growing large. Is this normal? WAL (Write-Ahead Log) space is cyclically reused after data is flushed to persistent storage. If the WAL directory appears large, it usually means data has not been flushed yet, or the WAL retention settings need tuning. WAL space is temporary — when measuring storage consumption, measure only the data directory (`data`). For WAL configuration options, see the [Configuration Guide](/user-guide/deployments-administration/configuration.md). ### Queries fail with `Too many files to read concurrently`. What should I do? This error means a single query is trying to scan more SST files than the per-query concurrency cap. A typical message: ``` Too many files to read concurrently: 1528, max allowed: 384 ``` Two common causes: - **Too many small SST files** — often seen after backfilling historical data with widely-spread timestamps, before compaction has merged them. - **The default file concurrency cap is too conservative** for your CPU/memory budget. **Diagnose**: use the [SSTS_MANIFEST](/reference/sql/information-schema/ssts-manifest.md) view to inspect file counts per table per day. Filter by `min_ts` to keep the result set manageable: ```sql SELECT table_id, date_trunc('day', min_ts), COUNT(*) AS files, SUM(num_rows) AS rows FROM information_schema.ssts_manifest WHERE min_ts > '2026-05-01 00:00:00' GROUP BY table_id, date_trunc('day', min_ts) ORDER BY files DESC; ``` **Mitigate**: 1. Raise the per-query file cap on the datanode (or standalone) under `[region_engine.mito]`: ```toml [region_engine.mito] max_concurrent_scan_files = 1024 ``` The default `384` is intentionally conservative. On hosts with more CPU and memory headroom, a larger value lets heavier scans finish without being rejected. Restart the affected datanodes after editing. 2. Trigger manual compaction using the Strict Window Compaction Strategy. If a single day holds many small files, use a window smaller than the default 1 day — see [Compaction](/user-guide/deployments-administration/manage-data/compaction.md): ```sql -- 1-hour window, parallelism=2 ADMIN COMPACT_TABLE('', 'swcs', 'window=3600,parallelism=2'); ``` ### What are GreptimeDB's scalability characteristics? - No strict limitations on table or column count; performance scales with primary key design rather than table count. - Automatic time-based organization within regions. - Manual distributed sharding via `PARTITION` clause — see [Table Sharding Guide](/user-guide/deployments-administration/manage-data/table-sharding.md). - Multi-tiered caching (write cache + LRU read cache) for optimal performance. - Object storage backend (S3/GCS/Azure Blob) provides virtually unlimited storage capacity. - Distributed query execution with MPP for large datasets. ### How does data distribution work? - Manual partitioning via `PARTITION` clause during table creation — see [Table Sharding Guide](/user-guide/deployments-administration/manage-data/table-sharding.md). - Time-based automatic organization within regions. - Manual region migration for load balancing — see [Region Migration Guide](/user-guide/deployments-administration/manage-data/region-migration.md). - Automatic region failover for disaster recovery — see [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md). ### How do I implement multi-tenancy? The recommended approach is to isolate tenants at the **database** or **table** level, rather than mixing every tenant into one shared table distinguished only by a tag. - **Database per tenant**: Give each tenant its own [database](/reference/sql/create.md#create-database). This keeps each tenant's data cleanly separated, with independent schema, TTL, and other table defaults. - **Table per tenant**: Within a shared database, give each tenant its own table (or table prefix). A good fit when tenants share configuration but you still want data-level separation. Don't worry about ending up with a large number of databases or tables. GreptimeDB's distributed architecture scales horizontally — tables are split into regions spread across datanodes, and you add datanodes as the table and region count grows. There is no hard limit on the number of tables, and performance depends on primary key design rather than table count (see [scalability characteristics](#what-are-greptimedbs-scalability-characteristics)). **Access control**: The open-source version does not enforce database- or table-level tenant isolation — its [authentication](/user-guide/deployments-administration/authentication/overview.md) authorizes a user against every catalog and schema, and the permission check only distinguishes read from write, not the target table. A read-only user can therefore still query any tenant's data. In open source you must enforce tenant isolation in your application layer (or via separate deployments). Enterprise and GreptimeCloud provide RBAC and ACL for true per-tenant access control. ### What disaster recovery options are available? GreptimeDB offers multiple disaster recovery strategies: - **Standalone DR**: Remote WAL + object storage. RPO=0 and an RTO in minutes are the targets; a standalone instance also keeps its metadata on local disk, so back that up too. - **Region Failover**: Automatic failover for individual regions with minimal downtime. - **Active-Active Failover** (Enterprise): Asynchronous request replication between two nodes. - **Cross-Region Single Cluster**: Spans three regions with zero RPO and region-level fault tolerance. - **Backup and Restore**: Periodic data backups with RPO depending on backup frequency. See [Disaster Recovery Overview](/user-guide/deployments-administration/disaster-recovery/overview.md). ### What should I do if ingestion fails with `Procedure poison key already exists`? This error blocks ingestion when a schema-on-write `ALTER TABLE` (typically adding a new column for an incoming attribute) fails after exhausting retries (12 by default), leaving a stale poison key in the metadata store. Subsequent writes that try to evolve the same table are then rejected. A typical error looks like: ``` Procedure poison key already exists with a different value Key: /__procedure_poison/table/ ``` **Recovery steps**: 1. Delete the poison key from the metadata store. Replace `--backend` and `--store-addrs` with your own — see [Metadata Interaction](/reference/command-lines/utilities/metadata-interaction.md#delete-key-value-pair): ```bash greptime cli meta del key \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ /__procedure_poison/table/ ``` 2. Run table reconciliation so that Metasrv and Datanode metadata converge. Use the schema and table that were affected — see [Table Reconciliation](/user-guide/deployments-administration/maintenance/table-reconciliation.md): ```sql USE ; ADMIN reconcile_table(''); ``` 3. If ingestion still fails with `Column ... not found in logical region`, the logical table's column metadata is out of sync with its physical table. Repair it via the CLI — see [Repair logical tables](/reference/command-lines/utilities/repair-logical-tables.md): ```bash greptime cli meta repair logical-tables \ --store-addrs=$ENDPOINT \ --backend=postgres-store \ --table-names= \ --schema-name= \ --catalog-name=greptime ``` **Troubleshooting tip**: If `ADMIN reconcile_*` returns `Done` immediately but Metasrv logs show no `Reconciling table:` entries, you are likely talking to a different Metasrv than expected — for example, another GreptimeDB cluster deployed in a separate namespace. Verify the topology (e.g. `kubectl get pods -A | grep meta`) before retrying. ### How do I monitor and troubleshoot GreptimeDB? GreptimeDB exposes Prometheus-compatible metrics and provides health check endpoints. For monitoring setup and troubleshooting guides, see [Monitoring Overview](/user-guide/deployments-administration/monitoring/overview.md). ## Open Source vs Enterprise vs Cloud ### What are the differences between GreptimeDB versions? - **Open Source**: Full distributed system, multiple protocol support, basic authentication. - **Enterprise**: Adds cost-based query optimizer, active-active failover, automatic scaling/indexing, RBAC/LDAP, and 24/7 support. - **GreptimeCloud**: Fully managed with resource and performance guarantees for production workloads, predictable pricing, and SLA guarantees. For a detailed feature comparison, see [Pricing & Features](https://greptime.com/pricing#differences). ### Does the GreptimeDB Dashboard have authentication? The open-source [GreptimeDB Dashboard](/getting-started/installation/greptimedb-dashboard.md) does not include built-in authentication. To restrict access, you can: - Place a reverse proxy (e.g., Nginx, Caddy) in front of the dashboard and configure HTTP Basic Auth or other authentication mechanisms. - Host the dashboard behind your organization's internal authentication system. **GreptimeDB Enterprise** provides a management console with built-in authentication and access control. ### What security features are available? **Open Source**: - Basic username/password authentication - User-level read/write access control - TLS/SSL support for connections **Enterprise / Cloud**: - Role-based access control (RBAC) - LDAP support - Team management and API keys - Data encryption at rest - Audit logging for compliance --- ## FAQ and Others In this section, we present the [most frequently asked questions](./faq.md) and some additional information. --- ## For AI Agents GreptimeDB is built to work with AI coding agents. An agent can deploy, configure, ingest, and query GreptimeDB on its own through standard protocols (OTLP, Prometheus, MySQL/PostgreSQL, SQL/PromQL), and the resources below help it do that reliably. ## Quickest start: one instruction The entry-point quickstart guide is hosted as a fetchable markdown file. Give your agent the prompt below and it takes it from there — fetching the guide at runtime, deciding whether GreptimeDB fits, picking the right install and write path, and navigating the docs via `llms.txt`. No installation required. > **AGENT ONBOARDING:** Read https://docs.greptime.com/SKILL.md and follow the instructions to use GreptimeDB with your AI agent — deploy, configure, ingest, and query. ## GreptimeDB MCP Server The [GreptimeDB MCP Server](../user-guide/integrations/mcp.md) implements the Model Context Protocol, so agents can securely explore and analyze your databases — list tables, read data, and run SQL, TQL (PromQL-compatible), and range queries. It enforces read-only access and supports data masking, and ships with built-in scenario templates for metrics, trace, and PromQL analysis, IoT monitoring, table operations, and log pipeline creation. ```shell pip install greptimedb-mcp-server ``` See the [greptimedb-mcp-server](https://github.com/GreptimeTeam/greptimedb-mcp-server) repo for transports (stdio, SSE, Streamable HTTP) and configuration. ## Table semantic layer Tables can carry a [semantic layer](../user-guide/concepts/semantic-layer.md) of `greptime.semantic.*` metadata recording what observability concept each one represents — signal type, source, metric type, unit, and more. An agent queries `information_schema.table_semantics` to understand your tables without inferring meaning from column names; the MCP Server's `describe_table` surfaces the same metadata. GreptimeDB stamps it automatically on OTLP and Prometheus ingestion, and you can set it yourself with `CREATE TABLE ... WITH (...)`. ## GreptimeDB Skills GreptimeDB already speaks open standards that most coding agents know. For our own features — pipelines, flows, triggers — a Skill teaches the agent the complete workflow and gives it the best results. Skills tell the agent *what* to do; the MCP Server provides the tools to *execute* it (for example, the pipeline skill uses `dryrun_pipeline` to verify a config before applying it). - **`greptimedb-quickstart`** — the entry point. When to use GreptimeDB, how to install, which write protocol to choose, how to query, plus pointers to deeper docs. Start here. - **`greptimedb-pipeline`** — parse, transform, and route logs (Learn → Create → Verify → Refine). - **`greptimedb-flow`** — continuous aggregation and materialized views. - **`greptimedb-trigger`** — alerting rules, including converting Prometheus alert rules to `CREATE TRIGGER` (Enterprise). - **`self-monitoring-export`** — for cluster incidents: infer the log export time range from the user's description, then export cluster self-monitoring logs and metrics for investigation. - **`greptimedb-performance-diagnosis`** — find the bottleneck behind slow queries, lagging or stalled ingestion, or high CPU and memory usage. - **`greptimedb-performance-tuning`** — tune server configuration and table options, including caches, write buffers, WAL, and ingestion throughput. - **`greptimedb-table-design`** — design or improve table schemas, primary keys, indexes, append-only or merge mode, and partitioning. - **`greptimedb-cluster-health-check`** — verify a deployment after a deploy or restart by checking readiness, object-store access, logs, and key metrics. - **`influxql-to-greptimedb-sql`** — convert InfluxQL queries to GreptimeDB SQL while identifying semantic differences in windows, fills, and series limits. Skills follow the [Agent Skills](https://agentskills.io/) open standard, so they work with Claude Code, OpenAI Codex CLI, GitHub Copilot, Cursor, and others. Every Skill is also hosted as a fetchable markdown file at `https://docs.greptime.com/skills//SKILL.md` (for example, [`greptimedb-pipeline`](https://docs.greptime.com/skills/greptimedb-pipeline/SKILL.md)), so an agent can load one at runtime without installing anything. To install a Skill persistently into your agent's config, use the `skills` CLI: ```shell npx skills add https://github.com/GreptimeTeam/docs/tree/main/skills/greptimedb-quickstart ``` See the [skills](https://github.com/GreptimeTeam/docs/tree/main/skills) repo for the full list and install commands. ## Machine-readable docs The whole documentation site is built to be consumed by agents, following the [llmstxt.org](https://llmstxt.org) standard: - [`llms.txt`](https://docs.greptime.com/llms.txt) — a structured index linking every documentation section with a short description. Point an agent at it to find the right page without crawling the site. - [`llms-full.txt`](https://docs.greptime.com/llms-full.txt) — the entire documentation concatenated into one file, for loading the full corpus at once. - **Any page as markdown** — append `.md` to any documentation URL to get the raw markdown of that page (for example, [`/user-guide/integrations/mcp.md`](https://docs.greptime.com/user-guide/integrations/mcp.md)). For ad-hoc questions, the **Ask AI** assistant on [docs.greptime.com](https://docs.greptime.com/) answers in plain language, grounded in the official documentation. ## References - [What GreptimeDB is doing for AI agents](https://greptime.com/blogs/2026-04-08-greptimedb-agent-friendly-infrastructure) - [Agent RCA Bench](https://rca-bench.greptime.com/) — 504 agent investigations across six models and 14 incidents, measuring how the query interface affects root-cause analysis - [Same Telemetry, Different Query Interfaces: Results from 504 Agent RCA Runs](https://greptime.com/blogs/2026-09-08-agent-rca-bench-interface-semantic-layer) --- ## Data Persistence and Indexing Like other LSM-tree storage engines, GreptimeDB persists data from memtables to durable storage such as a local filesystem or object storage. It uses [Apache Parquet][1] as the persistent file format. ## SST File Format Parquet is an open source columnar format that provides fast data querying and has already been adopted by many projects, such as Delta Lake. Parquet organizes data as row groups, column chunks, and pages. A row group contains one column chunk for each column, and each column chunk contains one or more pages. Pages are the units of encoding and compression; column chunks are the I/O units for reading selected columns. First, clustering data by column makes file scanning more efficient, especially when only a few columns are queried, which is very common in analytical systems. Second, values within a column tend to be similar, which improves compression with techniques such as dictionary encoding and run-length encoding (RLE). The following diagram from the Apache Parquet specification also shows the physical file layout: column chunks are stored by row group, while file metadata and its length are written in the footer. *Source: Apache Parquet [FileLayout.gif](https://github.com/apache/parquet-format/blob/master/doc/images/FileLayout.gif). Copyright 2014 The Apache Software Foundation, licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).* ## Data Persistence GreptimeDB provides a configuration item `region_engine.mito.global_write_buffer_size`, which is flush threshold of the total memory usage for all MemTables. When the size of data buffered in MemTables reaches that threshold, GreptimeDB will pick MemTables and flush them to SST files. ## Indexing Data in SST Files Parquet stores row-group column statistics such as minimum, maximum, and null count in each column chunk's metadata. Page metadata and optional column indexes can provide finer-grained statistics. ![A name predicate uses Parquet column statistics to skip one row group while retaining another as a read candidate.](/parquet-row-group-statistics.svg) For example, a query filtering for `name` = `Emily` can skip row group 0 because the maximum `name` value is `Charlie`. This avoids reading that row group. ## Index Files When an SST has one or more configured index outputs, GreptimeDB writes them to a Puffin file associated with that SST. An SST with no applicable index does not need a Puffin file. Puffin provides a container for index blobs and their metadata, allowing different index structures to share one file. ![Puffin](/puffin.png) GreptimeDB stores several index structures in the Puffin file as Blobs, including the inverted index, the skipping index (backed by a bloom filter), and the full-text index. The inverted index was the first one supported and is described in detail below. ## Inverted Index In version 0.7, GreptimeDB introduced the inverted index to accelerate queries. The inverted index is a common index structure used for full-text searches, mapping each word in the document to a list of documents containing that word. GreptimeDB applies this search-engine technique to indexes over time-series data. Search engines and time series databases operate in separate domains, yet the principle behind the applied inverted index technology is similar. This similarity requires some conceptual adjustments: 1. Term: In GreptimeDB, it refers to the column value of the time series. 2. Document: In GreptimeDB, it refers to the data segment containing multiple time series. The inverted index enables GreptimeDB to skip data segments that do not meet query conditions, thus improving scanning efficiency. ![Inverted index searching](/inverted-index-searching.png) The query above uses the inverted index to identify data segments where `job` equals `apiserver`, `handler` matches `.*users`, and `status` matches `4..`. It scans only those segments before applying the remaining filters. ### Inverted Index Format ![An inverted-index blob contains one index per column followed by footer metadata; each column index contains a null bitmap, posting bitmaps, and an FST.](/inverted-index-blob-layout.svg) GreptimeDB builds inverted indexes by column. Each column index contains a null bitmap, multiple posting bitmaps, and an FST. The blob footer records the offsets, sizes, and metadata needed to locate and decode the column indexes. The FST (Finite State Transducer) enables GreptimeDB to store mappings from column values to Bitmap positions in a compact format and provides excellent search performance and supports complex search capabilities (such as regular expression matching). The Bitmaps maintain a list of data segment IDs, with each bit representing a data segment. ### Index Data Segments GreptimeDB divides an SST file into multiple indexed data segments, with each segment housing an equal number of rows. This segmentation is designed to optimize query performance by scanning only the data segments that match the query conditions. For example, if a data segment contains 1024 rows and the list of data segments identified through the inverted index for the query conditions is `[0, 2]`, then only the 0th and 2nd data segments in the SST file—from rows 0 to 1023 and 2048 to 3071, respectively—need to be scanned. The number of rows in a data segment is controlled by the engine option `index.inverted_index.segment_row_count`, which defaults to `1024`. A smaller value means more precise indexing and often results in better query performance but increases the cost of index storage. By adjusting this option, a balance can be struck between storage costs and query performance. ## Unified Data Access Layer: OpenDAL GreptimeDB uses [OpenDAL][2] to provide a common access layer for local filesystems and object stores. Changing the configured storage backend does not migrate existing data. [1]: https://parquet.apache.org [2]: https://github.com/datafuselabs/opendal [3]: https://iceberg.apache.org/puffin-spec --- ## Memtable design A memtable is Mito's in-memory write buffer for a Region. It makes writes available to reads before a flush creates SST files. A Region version identifies the memtables and SST files that a scan may read. Together with a committed-sequence fence, it keeps the scan consistent while writes and flushes advance the current version. ## Write and flush lifecycle For a normal WAL-backed write, Mito uses this order: ```text write request | v WAL append -> mutable memtable -> publish committed sequence | freeze v immutable memtable -> SST write -> manifest edit ``` The Region worker assigns sequence numbers and a WAL entry ID before appending the mutation to the [write-ahead log](wal.md). If the WAL append fails, Mito does not update the memtable. After the memtable update succeeds, Mito publishes the committed sequence and the rows become visible to new reads. A Region configured with `skip_wal` omits the WAL append, but keeps the same memtable and visibility ordering. A flush freezes the mutable memtables and installs a new mutable set before starting the background SST write. New writes therefore continue without changing the frozen data. The flush writes the immutable memtables to SST files, then persists a manifest edit containing the files and the flushed WAL and sequence checkpoints. Only after that edit is durable does Mito remove the flushed memtables from the current Region version. If the flush fails, the immutable memtables remain available for a later attempt. ## Region versions and time partitions Each Region has one mutable `TimePartitions` container, which can hold more than one memtable: ```text Region version ├─ mutable TimePartitions │ ├─ [t0, t1) -> memtable │ └─ [t1, t2) -> memtable ├─ immutable memtables └─ SST files ``` Mito routes each row to a partition by its time-index value. Partition ranges are half-open and aligned to a fixed duration. The duration follows the Region's compaction time window; Mito uses one day until a compaction window is available. An out-of-order write can create an earlier partition alongside the latest one. Freezing a Region freezes all mutable time partitions together. Mito moves their memtables to the immutable list and creates a new `TimePartitions` container. A failed flush can leave more than one generation of immutable memtables, so reads and later flushes must not assume that the list contains a single item. ## Memtable implementations Mito selects a memtable implementation from the Region's SST format, primary-key encoding, and memtable options: ```text flat SST format (the default) or sparse primary-key encoding -> BulkMemtable memtable.type=bulk -> BulkMemtable, and forces flat SST primary_key SST with dense encoding (legacy) -> a legacy implementation ``` With the default engine configuration, a Region without an explicit SST format uses `flat`, so `BulkMemtable` is the normal path and the rest of this page describes it. The rules exist to prevent incompatible combinations: flat format or sparse primary-key encoding requires `BulkMemtable`, and explicitly selecting the bulk implementation forces flat format. ### BulkMemtable `BulkMemtable` stores writes as parts in the flat Arrow layout instead of inserting rows into per-series buffers: ```text BulkMemtable ├─ unordered_part │ └─ small BulkPart batches └─ parts ├─ BulkPart (Arrow RecordBatch) ├─ MultiBulkPart (raw RecordBatches) └─ EncodedBulkPart (in-memory Parquet) ``` Small parts accumulate in `unordered_part`; larger parts enter `parts` directly. Background memtable compaction merge-sorts eligible parts into a `MultiBulkPart` or encodes them as an `EncodedBulkPart`. Scans use part statistics to prune ranges, and flush can write encoded ranges to SST without decoding and encoding the rows again. For the design rationale and performance results, see [Scaling Time Series to Millions of Cardinalities: GreptimeDB's Flat Format](https://greptime.com/blogs/2025-12-22-flat-format). ### Legacy implementations Regions on the legacy `primary_key` SST format with dense primary-key encoding still use `TimeSeriesMemtable`, which groups rows by encoded primary key rather than storing flat parts. A Region with no primary-key columns gets `SimpleBulkMemtable` from the same builder. Both are compatibility code for existing tables and may be removed once the `primary_key` format is retired; new work targets the bulk and flat path. The removed `partition_tree` memtable is not a third implementation. The option parser accepts `memtable.type=partition_tree` for compatibility, but it does not recreate that implementation. The Region uses the bulk and flat path. ## Read snapshots A scan obtains the Region version and committed sequence together from `VersionControl`. It selects the version before applying the sequence fence. Reading the sequence separately before the version could pair that sequence with a later version after flush or compaction removes an older input, producing an incomplete snapshot. The selected version supplies mutable memtables, immutable memtables, and SST files. Mito first prunes sources by time range, then asks each memtable for ranges using the scan's projection, predicate, and sequence bounds. The scan merges the resulting ranges with SST ranges and applies the same ordering, deletion, and merge semantics across all sources. References held by the scan keep an older memtable alive even after a newer Region version removes it. ## Memory pressure Each memtable tracks its estimated heap allocation through the engine's write-buffer manager. Freezing a memtable removes its allocation from the mutable-memory count, but total usage includes the allocation until all references to that memtable are released. The mutable-memory count therefore tracks data that can still accept writes, while total usage continues to include memory retained by active scans. The global write-buffer limit causes workers to select Regions for flush. If memory remains above the configured limits, Mito stalls writes and can reject them at a higher threshold. An optional per-Region limit applies the same pressure to one hot Region without stalling unrelated Regions. Periodic, manual, and Region lifecycle operations can also request a flush. ## Constraints for changes Changes to memtable code must preserve these properties: - For a WAL-backed Region, append to the WAL before installing rows in a memtable. Publish the committed sequence only after installation succeeds. - Keep frozen memtables readable and retryable until the SST files and manifest edit are durable. - Obtain the Region version and committed sequence from the same `VersionControl` snapshot; never read the sequence separately before the version. - Preserve the ordering and metadata that scans and flushes need to apply the same deletion, deduplication, and merge rules across memtable and SST ranges. - Charge allocations to the write-buffer manager and release them only when the underlying memory can no longer be referenced. --- ## Metric Engine ## Overview The `Metric` engine stores workloads with many small metric tables. It maps those logical tables onto shared physical wide tables so they can reuse columns and metadata. This reduces per-table storage overhead and improves columnar compression. ## Concepts The `Metric` engine introduces two new concepts: "logical table" and "physical table". From the user's perspective, logical tables are exactly like ordinary ones. From a storage point-of-view, physical Regions are just regular Regions. ### Logical Table A logical table refers to user-defined tables. Just like any other ordinary table, its definition includes the name of the table, column definitions, index definitions etc. All operations such as queries or write-ins by users are based on these logical tables. Users don't need to worry about differences between logical and ordinary tables during usage. A logical table is virtual. The engine maps its read and write requests to the corresponding physical table instead of storing data for it directly. ### Physical Table A physical table is a table that actually stores data, possessing several physical Regions defined by partition rules. ## Architecture and Design The main design architecture of the `Metric` engine is as follows: ![Multiple logical tables map through the Metric engine to shared data and metadata Regions managed by Mito.](/metric-engine-architecture.svg) The `Metric` engine delegates physical storage and queries to the `Mito` engine. Each physical Region group contains a data Region, which stores rows from its mapped logical tables, and a metadata Region, which stores the logical-table and logical-column mappings. Logical tables associated with the same physical table share its partition layout. During writes, the engine records the logical table identity with each row. During reads, it adds a logical-table filter before scanning the physical Region. A logical table's route stores only the ID of its physical table; the physical table route resolves that to the Datanodes holding the Regions. Because logical routes do not name peers, migrating a physical Region rewrites one physical route instead of every logical route that maps to it. Logical tables support normal INSERT, DELETE, and SELECT operations. Direct writes to a physical Region are rejected because they would bypass the logical-table mapping; querying a physical table remains supported. Batch DDL operations reduce metadata work when many logical tables are created or updated together, such as during Prometheus Remote Write auto-creation or physical Region migration. --- ## Datanode(Datanode) ## Introduction A Datanode stores and processes Region data. A table can contain multiple Regions, but the Datanode does not own table-level routing. Frontend sends data requests by Region, while Metasrv controls Region placement and lifecycle. This boundary lets the same Region server host different storage engines without exposing their implementation to Frontend or Metasrv. ![Frontend sends Region requests to the Datanode Region server, while Metasrv exchanges lifecycle instructions through the heartbeat task. The Region server uses the local query engine and dispatches requests to the Mito, Metric, or File Region engine.](/datanode-architecture.svg) ## Components The main components are: - The Region server tracks open Regions and dispatches reads, writes, and lifecycle requests to the engine registered for each Region. - `Mito` is the primary time-series Region engine. `Metric` maps many logical metric Regions onto shared Mito Regions, and `File` exposes external files through the Region interface. - The local query engine executes Region query plans. It does not parse client SQL or perform cluster-wide planning. - The heartbeat task reports node and Region state to Metasrv and receives instructions such as open, close, upgrade, downgrade, and migration steps. - gRPC carries Region requests to the Datanode. HTTP exposes node diagnostics such as metrics and configuration. ## Region Request Lifecycle For a Mito write, the Region server selects Mito from the Region metadata. Mito appends the mutation to the WAL, applies it to a memtable, and later flushes the memtable to SST files. A Metric write is first rewritten with the logical-table identity and then delegated to its physical Mito Region. For a read, the local query engine executes the Region plan against a table provider backed by the Region engine. A Mito scan takes an immutable Region version, reads the relevant memtables and SST files, merges and deduplicates rows, and returns a stream of Arrow record batches. Region ownership can change without restarting the Datanode. Metasrv sends lifecycle instructions over the heartbeat stream; the Region server applies them to the engine and reports the new Region role and statistics in subsequent heartbeats. --- ## Query Engine ## Introduction GreptimeDB's query engine is built on [Apache DataFusion][1]. DataFusion supplies the logical and physical plan interfaces, optimizer framework, and execution runtime. GreptimeDB adds planners for its query languages, storage-aware optimizer rules, custom plan nodes, and distributed execution. DDL and other control-plane operations are dispatched by the statement executor. The query engine receives plans for data processing, including the input side of operations such as `INSERT ... SELECT`. ## Query Lifecycle 1. The SQL, PromQL, or log-query planner resolves tables through the catalog and produces a DataFusion logical plan. GreptimeDB plan extensions represent operations that DataFusion does not provide directly. 2. DataFusion analyzer and optimizer rules run together with GreptimeDB rules. These rules normalize expressions and types, rewrite time-range operations, push projections and filters toward scans, and introduce distributed plan nodes when required. 3. The physical planner converts the optimized logical plan into streaming operators. GreptimeDB then applies physical rules for scan parallelism, ordering, and distributed execution. 4. Execution pulls Arrow record batches through the physical plan. Storage scans receive the projection and predicates, and downstream operators consume the resulting stream without materializing the complete result first. Use [`EXPLAIN`](/reference/sql/explain.md) to inspect the logical and physical plans. `EXPLAIN ANALYZE` also executes the plan and reports runtime metrics. ## Data Representation GreptimeDB uses [Apache Arrow][2] record batches as its in-memory data representation. A record batch contains equal-length column arrays and a schema. Query operators exchange streams of these batches, which keeps the execution path columnar from Region scans through result encoding. ## Indexing Index construction and persistent index formats belong to the storage engine. The query layer supplies predicates and projections to a scan; Mito then uses time ranges, Parquet statistics, and indexes to avoid reading data that cannot match. See [Data Persistence and Indexing](./data-persistence-indexing.md). ## Distributed Execution In distributed mode, the Frontend plans the cluster-wide query and Datanodes execute Region-local subplans. [`MergeScan`](../frontend/distributed-querying.md) is the boundary between those stages. [1]: https://datafusion.apache.org/ [2]: https://arrow.apache.org/ --- ## Storage Engine ## Introduction Mito is GreptimeDB's default storage engine. It uses an [LSM tree][1] and is designed for time-series workloads rather than as a general-purpose embedded storage engine. ## Architecture The picture below shows the architecture and process procedure of the storage engine. ![Architecture](/storage-engine-arch.png) The architecture is the same as a traditional LSMT engine: - [WAL][2] - Guarantees high durability for data that is not yet being flushed. - Based on the `Log Store` API, thus it doesn't care about the underlying storage media. - Log records of the WAL can be stored on the local disk, or in a remote log service such as Kafka (remote WAL) that implements the `Log Store` API. - [Memtables](memtable.md): - Mito routes rows by time index into mutable memtables. - A flush freezes the mutable memtables, installs a new mutable set for writes, and writes the frozen memtables to SST files. - SST - The full name of SST, aka SSTable is `Sorted String Table`. - `Immutable memtable` is flushed to persistent storage and produces an SST file. - Rows in an SST are sorted by primary key and time index; see [Data Layout in SST Files](#data-layout-in-sst-files). - Compactor - Small `SST` is merged into large `SST` by the compactor via compaction. - The default compaction strategy is [TWCS][3]. Compaction groups SST files into time windows and, together with TTL, removes expired data. See [Compaction](/user-guide/deployments-administration/manage-data/compaction.md). - Manifest - The manifest stores the metadata of the engine, such as the metadata of the `SST`. - Cache - Speed up queries. [1]: https://en.wikipedia.org/wiki/Log-structured_merge-tree [2]: https://en.wikipedia.org/wiki/Write-ahead_logging [3]: https://cassandra.apache.org/doc/latest/cassandra/operating/compaction/twcs.html ## Data Model The data model provided by the storage engine is between the `key-value` model and the tabular model. ```txt tag-1, ..., tag-m, timestamp -> field-1, ..., field-n ``` Each row of data contains multiple tag columns, one timestamp column, and multiple field columns. - `0 ~ m` tag columns - Tag columns can be nullable. - Specified during table creation using `PRIMARY KEY`. - Must include one timestamp column - Timestamp column cannot be null. - Specified during table creation using `TIME INDEX`. - `0 ~ n` field columns - Field columns can be nullable. - Data is sorted by tag columns and timestamp column. ### Region Data in the storage engine is stored in `regions`, which are logical isolated storage units within the engine. Rows within a `region` must have the same `schema`, which defines the tag columns, timestamp column, and field columns within the `region`. The data of tables in the database is stored in one or multiple `regions`. ## Data Layout in SST Files When a memtable is flushed, Mito writes its rows into immutable [Apache Parquet](https://parquet.apache.org) SST files. For the Parquet file format itself and how SST files are indexed, see [Data Persistence and Indexing](data-persistence-indexing.md). Within an SST file, rows are sorted by `(primary key, time index)`. Rows that share the same primary key (the tag columns) belong to the same time-series and are stored contiguously, ordered by timestamp. This locality is what makes scanning a single series cheap and improves compression. For append-only tables without a primary key, rows are sorted by the time index alone. For example, consider a table that stores host metrics: ```sql CREATE TABLE host_metrics ( host STRING, region STRING, ts TIMESTAMP TIME INDEX, cpu DOUBLE, memory DOUBLE, PRIMARY KEY (host, region) ); ``` Mito groups rows by primary key and orders them by time, so the data within an SST conceptually looks like: | host | region | ts | cpu | memory | | --- | --- | --- | --- | --- | | host-a | us-east | 10:00 | 0.42 | 7.1 | | host-a | us-east | 10:01 | 0.47 | 7.4 | | host-a | us-west | 10:00 | 0.31 | 6.8 | | host-b | us-east | 10:00 | 0.80 | 8.6 | Besides the table columns, Mito stores three internal columns in each SST file so it can merge, deduplicate, and apply deletes correctly when reading from multiple memtables and SST files: - `__primary_key`: the encoded primary key (tags) of the row. - `__sequence`: the sequence number of the row. - `__op_type`: the operation type of the row (put or delete). Each Parquet SST is split into row groups, the unit that Parquet can read or skip independently. Every row group carries column statistics such as min value, max value, and null count. Mito also records file-level metadata for each SST, including the time range, row count, row-group count, available indexes, and the primary-key range. These statistics drive the scan pruning described below. Mito supports two SST formats: `flat` and `primary_key`. `flat` is the default for new tables and works well across primary-key cardinalities, including high-cardinality keys. `primary_key` is the legacy format kept for compatibility with older tables. See [SST format](/reference/sql/create.md#create-a-table-with-sst-format) and the [table design guide](/user-guide/deployments-administration/performance-tuning/design-table.md#sst-format) for more details. ![The default flat Mito SST layout combines file-level metadata with Parquet row groups containing data columns and merge metadata.](/mito-sst-layout.svg) An SST may span more than one compaction time window. ## Scan Pruning Mito avoids reading data that cannot match a query by combining several pruning steps, from coarse to fine: 1. **Time-range pruning.** Files and memtables whose time range does not intersect the query's time range are skipped before opening any reader. This is usually the cheapest and most effective step for time-series queries. 2. **Row-group statistics.** If a row group's min-max statistics prove that no row can match a predicate, the whole row group is skipped. 3. **Indexes.** Inverted, skipping, and full-text indexes provide more selective pruning for predicates that statistics cannot resolve. See [Data Persistence and Indexing](data-persistence-indexing.md). --- ## Write-Ahead Logging ## Introduction Mito buffers writes in [memtables](memtable.md) before flushing them to SST files. It first appends each Region's mutations to the write-ahead log (WAL), so data that has not reached an SST can be recovered. The WAL uses a common log-store abstraction with local raft-engine and remote Kafka providers. ## Write and Recovery Cycle The order of a normal write is: 1. The Region worker assigns sequence numbers and a WAL entry ID. 2. It appends the mutations to the WAL. If the append fails, the mutations are not applied to the memtable. 3. After the append succeeds, Mito writes the mutations to the memtable and publishes the new committed sequence. 4. A flush writes immutable SST files and persists a manifest edit containing the new files and `flushed_entry_id`. 5. After the manifest edit is durable, WAL entries through `flushed_entry_id` are marked obsolete. The log store may reclaim them later. The manifest is the recovery boundary. On a normal reopen, Mito rebuilds the Region from the manifest and replays WAL entries starting at `flushed_entry_id + 1`. Region transitions may supply a later replay checkpoint, but they never replay entries before the persisted flush boundary. ## Namespace WAL entries are isolated by Region, not by table. Each append and read identifies a Region namespace so one Region can be replayed or truncated independently. The local raft-engine provider uses the Region ID as its namespace ID. Kafka keeps Region identity within the provider's topic-backed log. ## Synchronous/Asynchronous flush For the local raft-engine provider, `sync_write` controls whether an append waits for the log to be synced to durable storage. It defaults to `false`. Asynchronous writes reduce latency but can lose recently acknowledged entries if the host fails before buffered data is synced. Kafka WAL durability is controlled by its producer and cluster settings instead of this local option. --- ## Arrangement This page describes state used by Flownode's legacy streaming mode. Batching mode does not use an Arrangement. Arrangement stores the state in the dataflow's process. It stores the streams of update flows for further querying and updating. The arrangement essentially stores key-value pairs with timestamps to mark their change time. Internally, the arrangement receives tuples like `((Key Row, Value Row), timestamp, diff)` and stores them in memory. One can query key-value pairs at a certain time using the `get(now: Timestamp, key: Row)` method. The arrangement also assumes that everything older than a certain time (also known as the low watermark) has already been ingested to the sink tables and does not keep a history for them. :::tip NOTE The arrangement allows for the removal of keys by setting the `diff` to -1 in incoming tuples. Moreover, if a row has been previously added to the arrangement and the same key is inserted with a different value, the original value is overwritten with the new value. ::: --- ## Batching Mode This guide provides a brief overview of the batching mode in `flownode`. It's intended for developers who want to understand the internal workings of this mode. ## Overview The batching mode in `flownode` is designed for continuous data aggregation. It periodically executes a user-defined SQL query over small, discrete time windows. This is in contrast to the legacy streaming path, which processes data as it arrives and is retained for compatibility but deprecated for new workloads. The core idea is to: 1. Define a `flow` with a SQL query that aggregates data from a source table into a sink table. 2. The query typically includes a time window function (e.g., `date_bin`) on a timestamp column. 3. When new data is inserted into the source table, the system marks the corresponding time windows as "dirty." 4. A background task runs on its own cadence, consumes the pending dirty windows at its next evaluation, and re-runs the aggregation query for those time ranges. 5. The results are then inserted into the sink table, effectively updating the aggregated view. ## Architecture The batching mode consists of several key components that work together to achieve this continuous aggregation. As shown in the diagram below: ```mermaid flowchart TB SRC["Source table"] -->|"write"| ENG["BatchingEngine"] ENG -->|"mark affected windows"| DTW("DirtyTimeWindowspending ranges per task") DTW -->|"read at next evaluation"| RUN["BatchingTaskschedule or polling cadence"] RUN -.->|"clear processed"| DTW RUN -->|"windowed: INSERT INTO sink SELECTwith time predicates"| FE["Frontend"] RUN -->|"TQL or unprunable plan:unfiltered full query"| FE FE -->|"scan"| SRC FE -->|"upsert by time window"| SINK["Sink table"] ``` ### `BatchingEngine` The `BatchingEngine` is the heart of the batching mode. It's a central component that manages all active flows. Its primary responsibilities are: - **Task Management**: It maintains a map of `FlowId` to `BatchingTask`. It handles the creation, deletion, and retrieval of these tasks. - **Event Dispatching**: When new data arrives (via `handle_inserts_inner`) or when time windows are explicitly marked as dirty (`handle_mark_dirty_time_window`), the `BatchingEngine` identifies which flows are affected and forwards the information to the corresponding `BatchingTask`s. ### `BatchingTask` A `BatchingTask` represents a single, independent data flow. Each task is associated with one `flow` definition and runs in its own asynchronous loop. - **Configuration (`TaskConfig`)**: This struct holds the immutable configuration for a flow, such as the SQL query, source and sink table names, and time window expression. - **State (`TaskState`)**: This contains the dynamic, mutable state of the task, most importantly the `DirtyTimeWindows`. - **Execution Loop**: The task runs an infinite loop (`start_executing_loop`) that: 1. Checks for a shutdown signal. 2. Sleeps until its next evaluation time. A task with an evaluation schedule sleeps until the next scheduled time; an adaptive task sleeps for a polling interval derived from the time window size and the minimum refresh duration. 3. Generates a new query plan (`gen_insert_plan`) based on the current set of dirty time windows. 4. Executes the query (`execute_logical_plan`) against the database. 5. Cleans up the processed dirty windows. ### `TaskState` and `DirtyTimeWindows` - **`TaskState`**: This struct tracks the runtime state of a `BatchingTask`, including the `dirty_time_windows` that determine its pending work. - **`DirtyTimeWindows`**: This data structure tracks which time windows have received new data since the last query execution. It stores a set of non-overlapping time ranges. The execution loop uses it to build a `WHERE` clause that selects only the dirty windows from the source table. ### `TimeWindowExpr` The `TimeWindowExpr` is a helper utility for dealing with time window expressions like `date_bin`. - **Evaluation**: It can take a timestamp and evaluate the time window expression to determine the start and end of the window that the timestamp falls into. - **Window Size**: It can also determine the size (duration) of the time window from the expression. The same calculation is used to mark dirty windows and generate the source-table filters. ## Query Execution Flow Here's a simplified step-by-step walkthrough of how a query is executed in batch mode: 1. **Data Ingestion**: New data is written to a source table. 2. **Marking Dirty**: The `BatchingEngine` receives a notification about the new data. It uses the `TimeWindowExpr` associated with each relevant flow to determine which time windows are affected by the new data points. These windows are then added to the `DirtyTimeWindows` set in the corresponding `TaskState`. Marking a window dirty does not wake the task. 3. **Next Evaluation**: The `BatchingTask`'s execution loop reaches its next evaluation, either at a scheduled time or after its adaptive polling interval, and consumes the pending dirty windows. 4. **Plan Generation**: The task calls `gen_insert_plan`. This method: - Inspects the `DirtyTimeWindows`. - Generates a series of `OR`'d `WHERE` clauses (e.g., `(ts >= 't1' AND ts < 't2') OR (ts >= 't3' AND ts < 't4') ...`) that cover the dirty windows. - Rewrites the original SQL query to include this new filter, ensuring that only the necessary data is processed. 5. **Execution**: The modified query plan is sent to the `Frontend` for execution. The database processes the aggregation on the filtered data. 6. **Upsert**: The results are inserted into the sink table. The sink table is typically defined with a primary key that includes the time window column, so new results for an existing window will overwrite (upsert) the old ones. 7. **State Update**: The `DirtyTimeWindows` set is cleared of the windows that were just processed. The task then goes back to sleep until the next interval. --- ## Dataflow Flownode has two internal execution paths: - **Batching mode** is the primary path for aggregation and TQL workloads. It evaluates queries over persisted source data and writes materialized results to a sink table. - **Streaming mode** is the legacy path retained for compatibility and deprecated for new workloads. It incrementally processes rows mirrored from Frontend as they arrive. Users do not select the mode directly. When a Flow is created, GreptimeDB derives the path from the source tables and then the query, in this order: ```mermaid flowchart LR MISS{"any source tablemissing?"} MISS -->|"yes, deferred"| PEND["pendingbatching Flow"] MISS -->|"yes, otherwise"| ERR["rejected"] MISS -->|"no"| TTL{"any source tablettl = 'instant'?"} TTL -->|"yes"| STREAM["Streaming mode"] TTL -->|"no"| TQL{"TQL query?"} TQL -->|"yes"| BATCH["Batching mode"] TQL -->|"no"| AGG{"plan has Aggregateor Distinct?"} AGG -->|"yes"| BATCH AGG -->|"no"| STREAM ``` The source-table checks come first, so a Flow that aggregates a source table with `ttl = 'instant'` runs in streaming mode rather than batching mode. A Flow whose source table does not exist yet is rejected unless it is created with `WITH (defer_on_missing_source = true)`, which produces a pending batching Flow. ## Batching mode Batching mode reuses GreptimeDB's query engine instead of maintaining an operator graph for every incoming row. For a time-windowed Flow, its main loop is: 1. A source-table write marks the affected time windows as dirty. 2. A `BatchingTask` runs on its evaluation schedule or adaptive polling cadence and collects the pending dirty windows at that evaluation. Marking a window dirty does not wake the task. 3. The task adds time predicates for those windows to the Flow query and asks Frontend to execute it against the source tables. 4. The query result is inserted into the sink table, updating the materialized result for windows that were evaluated. 5. Successfully processed windows are removed from the dirty set. Failed work remains available for a later evaluation. TQL Flows, and evaluation-interval Flows whose plan cannot be pruned safely by dirty windows, run the complete query rather than a time-filtered one. On that path the dirty set is only a scheduling signal. This path also lets Flow use query-engine features that the streaming renderer does not implement. See [Batching Mode](./batching_mode.md) for the task and dirty-window components. ## Streaming mode The `dataflow` module (see `flow::compute` module) is the core computing module of `flow`. It takes a SQL query and transforms it into flow's internal execution plan. This execution plan is then rendered into an actual dataflow, which is essentially a directed acyclic graph (DAG) of functions with input and output ports. New row changes drive the graph incrementally. The renderer supports map/filter/project and reduce operations. Join and union plan nodes exist, but their streaming renderers are not implemented. Internally, the dataflow handles data in row format, using a tuple `(row, time, diff)`. Here, `row` represents the actual data being passed, which may contain multiple `Value` objects. `time` is the system time which tracks the progress of the dataflow, and `diff` typically represents the insertion or deletion of the row (+1 or -1). Therefore, the tuple represents the insert/delete operation of the `row` at a given system `time`. Stateful operators keep indexed traces of these changes in an [Arrangement](./arrangement.md). --- ## Flownode(Flownode) ## Introduction `Flownode` provides Flow computation capabilities to the database. `Flownode` manages `flows` which are tasks that receive data from the `source` and send data to the `sink`. `Flownode` support both `standalone` and `distributed` mode. In `standalone` mode, `Flownode` runs in the same process as the database. In `distributed` mode, `Flownode` runs in a separate process and communicates with the database through the network. There are two execution modes for a flow: - **Batching Mode**: The active mode for continuous data aggregation. It periodically executes a user-defined SQL query over small, discrete time windows. Aggregation and TQL queries use this mode. For more details, see [Batching Mode](./batching_mode.md). - **Streaming Mode (deprecated)**: The original mode where data is processed as it arrives. It is kept for legacy compatibility and is not recommended for new workloads. ## Components A `Flownode` contains all the components needed to execute a flow. The specific components involved depend on the execution mode. At a high level, the key parts are: - **Flow Manager**: A central component responsible for managing the lifecycle of all flows. - **Task Executor**: The runtime environment where the flow logic is executed. In batching mode, this is a `BatchingTask`; in the deprecated streaming mode, this is typically a `FlowWorker`. - **Flow Task**: Represents a single, independent data flow, containing the logic for transforming data from a source to a sink. --- ## Distributed Querying Frontend and Datanode use the same DataFusion-based query engine. In distributed mode, Frontend adds a planning step that separates work performed by Datanodes from work completed by Frontend. ![Frontend query](/frontend-query.png) ## Dist Planner The distributed planner rewrites the logical plan. It pushes compatible operators toward table scans and wraps remote subplans in `MergeScan` nodes. Partition predicates are also used to prune Regions before the remote work is scheduled. Whether an operator can be pushed down depends on the plan shape and the operator's properties. Unsupported parts remain on Frontend. The original design and its commutativity rules are described in the [distributed planner RFC](https://github.com/GreptimeTeam/greptimedb/blob/main/docs/rfcs/2023-05-09-distributed-planner.md). ## Dist Plan A remote input is a complete logical subplan, not just a table scan. Frontend serializes the subplan in [Substrait](https://substrait.io) format and sends a Region-specific request to the Datanode that owns the data. The Datanode plans and executes it locally, then streams the result back. Frontend merges the remote streams and executes any operators that were not pushed down. --- ## Frontend(Frontend) Frontend is GreptimeDB's stateless request-orchestration service. The server layer terminates protocols and converts wire messages; Frontend supplies the database behavior behind those handlers, including permission checks, statement execution, routing, and distributed query planning. Frontend does not store table data. It caches catalog and route metadata obtained from Metasrv, and Metasrv invalidates those caches through heartbeat responses when metadata changes. ## Core Functions - Provide query and ingestion behavior for the supported [protocols][1]. - Resolve catalogs, schemas, tables, and Region routes. - Validate permissions before executing a request. - Plan distributed queries and merge results from Datanodes. - Convert table-level writes and deletes into Region requests. ## Architecture ### Key Components - Protocol handlers adapt SQL, PromQL, gRPC ingestion, and observability protocols to Frontend's internal request interfaces. - The catalog and partition managers provide table metadata, partition rules, and Region routes. - The statement executor dispatches queries, DML, and DDL to their respective execution paths. - The distributed planner replaces table scans with `MergeScan` plans that can run across Datanodes. ### Request Flow The request path depends on the operation. #### Queries 1. A protocol handler creates the query context and performs authentication and permission checks. 2. The language-specific planner produces a logical plan. In distributed mode, the planner uses partition metadata to select Regions and constructs a distributed plan. 3. Frontend sends Region subplans to the owning Datanodes. Datanodes execute them against local Region engines and return streams of Arrow record batches. 4. Frontend runs the remaining operators, merges the streams, and formats the result for the client protocol. #### Writes and deletes 1. Frontend validates the request against the table schema. Protocols that support schema-on-write may create a missing table or add columns before retrying the write. 2. The partition rule assigns rows to Regions. Frontend builds one Region request per target and routes it to the current Region leader. 3. The Datanode's Region server dispatches each request to the Region engine. In standalone mode, the same request is sent to an embedded Region server instead of over RPC. #### DDL The statement executor converts DDL into a task. In distributed mode, Metasrv runs that task as a persisted procedure, updates metadata, and coordinates Region operations on Datanodes. Standalone mode uses the same statement boundary with local implementations of the metadata and procedure services. ### Deployment The following picture shows a typical deployment of GreptimeDB in the cloud. The `Frontend` instances form a cluster to serve the requests from clients: ![frontend](/frontend.png) ## Details - [Table Sharding][2] - [Distributed Querying][3] [1]: /user-guide/protocols/overview.md [2]: ./table-sharding.md [3]: ./distributed-querying.md --- ## Table Sharding(Frontend) GreptimeDB shards a table into Regions. Partition expressions define which rows belong to each Region, while Region routes define which Datanode currently owns each Region. ## Partition A partition is a logical row set described by an expression over one or more columns. The partition layout must cover the table's input domain so each row has one target Region. See [Table Sharding](/user-guide/deployments-administration/manage-data/table-sharding.md) for the SQL syntax and supported expressions. ## Region Each partition maps to one Region. Region IDs remain the storage and routing identity used by Frontend, Datanode, and Metasrv. Multiple Regions from the same table may be placed on one Datanode. The relationship between partition and region can be viewed as the following diagram: ```text ┌───────┐ │ │ │ Table │ │ │ └───┬───┘ │ Range [Start, end) │ Horizontally Split Data ┌──────────────────┼──────────────────┐ │ │ │ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │ │ │ │ │ │ │ Partition │ │ Partition │ │ Partition │ │ │ │ │ │ │ │ P0 │ │ P1 │ │ Px │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ │ │ │ One-to-one mapping of ┌───────┼──────────────────┼───────┐ │ Partition and Region │ │ │ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ ┌─────▼─────┐ │ │ │ │ │ │ │ │ │ │ Region │ │ Region │ │ │ Region │ │ │ │ │ │ │ │ │ │ │ R0 │ │ R1 │ │ │ Ry │ │ └───────────┘ └───────────┘ │ └───────────┘ │ │ └──────────────────────────────────┘ Could be placed in one Datanode ``` ## Routing and Pruning For writes, Frontend evaluates the partition rule for each row, groups rows by Region, and sends Region requests to the current leaders from the route table. For queries, the distributed planner compares query predicates with the partition expressions. It scans only Regions that can satisfy the predicates. If partition metadata is missing or cannot be interpreted safely, the planner falls back to all Regions rather than risk omitting data. ## Changing the Partition Layout [Repartitioning](/user-guide/deployments-administration/manage-data/repartition.md) changes an existing layout through explicit split and merge operations. Metasrv runs the change as a persisted procedure, updates the Region routes and partition expressions, and invalidates stale table-route caches. New requests use the published layout after their Frontend refreshes that metadata. --- ## Getting started This page describes how to run GreptimeDB from source in your local environment. ## Prerequisite ### System & Architecture At the moment, GreptimeDB supports Linux (both amd64 and arm64), macOS (both amd64 and Apple Silicon), and Windows. ### Build Dependencies - [Git](https://git-scm.com/book/en/v2/Getting-Started-The-Command-Line) (optional; needed to clone the repository, not to build it) - C/C++ Toolchain: provides essential tools for compiling and linking. This is available either as `build-essential` on ubuntu or a similar name on other platforms. - [Rustup][1]. The repository pins the required nightly toolchain in `rust-toolchain.toml`. - Protobuf ([guide][2]) - Compile the proto file - Note that the version needs to be >= 3.15. You can check it with `protoc --version` - Machine: 16GB of memory or more is recommended. On a smaller machine, use [mold](https://github.com/rui314/mold) to reduce memory usage during linking. [1]: [2]: ## Compile and Run Start GreptimeDB standalone instance in just a few commands! ```shell git clone https://github.com/GreptimeTeam/greptimedb.git cd greptimedb cargo run -- standalone start ``` Next, you can choose the protocol you like to interact with in GreptimeDB. Or if you just want to build the server without running it: ```shell cargo build # --release ``` The artifacts can be found under `$REPO/target/debug` or `$REPO/target/release`, depending on the build mode (whether the `--release` option is passed) ## Unit test GreptimeDB is well-tested, the entire unit test suite is shipped with source code. To test them, run with [nextest](https://nexte.st/index.html). To install nextest using cargo, run: ```shell cargo install cargo-nextest --locked ``` Or you can check their [docs](https://nexte.st/docs/installation/pre-built-binaries/) for other ways to install. After nextest is ready, you can run the test suite with: ```shell cargo nextest run --workspace --features pg_kvbackend,mysql_kvbackend ``` ## Docker We also provide prebuilt binaries via Docker, available on Docker Hub: [https://hub.docker.com/r/greptime/greptimedb](https://hub.docker.com/r/greptime/greptimedb) --- ## How to trace GreptimeDB GreptimeDB uses Rust's [tracing](https://docs.rs/tracing/latest/tracing/) framework for code instrument. For the specific details and usage of tracing, please refer to the official documentation of tracing. By transparently transmitting `trace_id` and other information on the entire distributed system, we can record the function call chain of the entire distributed link, know the time of each tracked function take and other related information, so as to monitor the entire system. ## Define tracing context in RPC Because the tracing framework does not natively support distributed tracing, we need to manually pass information such as `trace_id` in the RPC message to correctly identify the function calling relationship. We use standards based on [w3c](https://www.w3.org/TR/trace-context/#traceparent-header-field-values) to encode relevant information into `tracing_context` and attach the message to the RPC header. Mainly defined in: - `frontend` interacts with `datanode`: `tracing_context` is defined in [`RegionRequestHeader`](https://github.com/GreptimeTeam/greptime-proto/blob/main/proto/greptime/v1/region/server.proto) - `frontend` interacts with `metasrv`: `tracing_context` is defined in [`RequestHeader`](https://github.com/GreptimeTeam/greptime-proto/blob/main/proto/greptime/v1/meta/common.proto) - Client interacts with `frontend`: `tracing_context` is defined in [`RequestHeader`](https://github.com/GreptimeTeam/greptime-proto/blob/main/proto/greptime/v1/common.proto) ## Pass tracing context in RPC call We build a `TracingContext` structure that encapsulates operations related to the tracing context. [Related code](https://github.com/GreptimeTeam/greptimedb/blob/main/src/common/telemetry/src/tracing_context.rs) GreptimeDB uses `TracingContext::from_current_span()` to obtain the current tracing context, uses the `to_w3c()` method to encode the tracing context into a w3c-compliant format, and attaches it to the RPC message, so that the tracing context is correctly distributed passed within the component. The following example illustrates how to obtain the current tracing context and pass the parameters correctly when constructing the RPC message, so that the tracing context is correctly passed among the distributed components. ```rust let request = RegionRequest { header: Some(RegionRequestHeader { tracing_context: TracingContext::from_current_span().to_w3c(), ..Default::default() }), body: Some(region_request::Body::Alter(request)), }; ``` On the receiver side of the RPC message, the tracing context needs to be correctly decoded and used to build the first `span` to trace the function call. For example, the following code will correctly decode the `tracing_context` in the received RPC message using the `TracingContext::from_w3c` method. And use the `attach` method to attach the context message to the newly created `info_span!("RegionServer::handle_read")`, so that the call can be tracked across distributed components. ```rust ... let tracing_context = request .header .as_ref() .map(|h| TracingContext::from_w3c(&h.tracing_context)) .unwrap_or_default(); let result = self .handle_read(request) .trace(tracing_context.attach(info_span!("RegionServer::handle_read"))) .await?; ... ``` ## Use `tracing::instrument` to instrument the code We use the `instrument` macro provided by tracing to instrument the code. We only need to annotate the `instrument` macro in the function that needs to be instrument. The `instrument` macro will print every function parameter on each function call into the span in the form of `Debug`. For parameters that do not implement the `Debug` trait, or the structure is too large and has too many parameters, resulting in a span that is too large. If you want to avoid these situations, you need to use `skip_all` to skip printing all parameters. ```rust #[tracing::instrument(skip_all)] async fn instrument_function(....) { ... } ``` ## Code instrument across runtime Rust's tracing library will automatically handle the nested relationship between instrument functions in the same runtime, but if a function call across the runtime, tracing library cannot automatically trace such calls, and we need to manually pass the context across the runtime. ```rust let tracing_context = TracingContext::from_current_span(); let handle = runtime.spawn(async move { handler .handle(query) .trace(tracing_context.attach(info_span!("xxxxx"))) ... }); ``` For example, the above code needs to perform tracing across runtimes. We first obtain the current tracing context through `TracingContext::from_current_span()`, create a span in another runtime, and attach the span to the current context, and we are done. The hidden code points that span the runtime are eliminated, and the call chain is correctly traced. --- ## How to use tokio-console in GreptimeDB This document introduces how to use the [tokio-console](https://github.com/tokio-rs/console) in GreptimeDB. First, build GreptimeDB with feature `cmd/tokio-console`. Also the `tokio_unstable` cfg must be enabled: ```bash RUSTFLAGS="--cfg tokio_unstable" cargo build -F cmd/tokio-console ``` Then start GreptimeDB with the tokio console binding address config: `--tokio-console-addr`. For example: ```bash greptime --tokio-console-addr="127.0.0.1:6669" standalone start ``` Now you can use `tokio-console` to connect to GreptimeDB's tokio console subscriber: ```bash tokio-console [TARGET_ADDR] ``` "TARGET_ADDR" defaults to "\". :::tip Note You can refer to [tokio-console](https://github.com/tokio-rs/console) to see the installation of `tokio-console`. ::: --- ## How to write a gRPC SDK for GreptimeDB GreptimeDB's public gRPC SDKs are ingestion clients. Queries normally use SQL or PromQL through their standard clients. A new SDK should therefore focus on writes and deletes unless it has a separate requirement, and follow the `greptimedb-ingester-` naming convention. See the [gRPC SDK overview](/user-guide/ingest-data/for-iot/grpc-sdks/overview.md) for the user-facing API. ## `GreptimeDatabase` Service Generate client stubs from the [`GreptimeDatabase` Protobuf definition](https://github.com/GreptimeTeam/greptime-proto/blob/main/proto/greptime/v1/database.proto). Do not maintain a handwritten copy of the messages or service definition. The service provides a unary method and a client-streaming method: ```protobuf service GreptimeDatabase { rpc Handle(GreptimeRequest) returns (GreptimeResponse); rpc HandleRequests(stream GreptimeRequest) returns (GreptimeResponse); } ``` `Handle` returns one response for one request. It is the usual choice for an SDK's insert and delete APIs. `HandleRequests` is a [client-streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#client-streaming-rpc). The server returns a cumulative response only after the client closes the request stream. An SDK that exposes streaming must document this acknowledgement boundary and bind the stream to one endpoint. ### `GreptimeRequest` The `GreptimeRequest` is a Protobuf message defined like this: ```protobuf message GreptimeRequest { RequestHeader header = 1; oneof request { InsertRequests inserts = 2; QueryRequest query = 3; DdlRequest ddl = 4; DeleteRequests deletes = 5; RowInsertRequests row_inserts = 6; RowDeleteRequests row_deletes = 7; } } ``` A client must populate `RequestHeader` with the database context and authentication expected by the server. Set exactly one request variant. The message also contains query and DDL variants used by internal callers. The public ingester API should not expose them: `GreptimeDatabase` does not return query result streams. GreptimeDB accepts row-oriented `RowInsertRequests` and column-oriented `InsertRequests`. Row-oriented requests are the default for public ingestion APIs. A column-native client may use the column form, but it must keep column lengths consistent and preserve null values, timestamp precision, data types, and column semantic types during conversion. Deletes have the same row-oriented and column-oriented distinction. Expose only the forms that the SDK can map without losing type information. ### `GreptimeResponse` The `GreptimeResponse` is a Protobuf message defined like this: ```protobuf message GreptimeResponse { ResponseHeader header = 1; oneof response {AffectedRows affected_rows = 2;} } ``` On success, the response contains a successful header and `affected_rows`. Treat that value as the number acknowledged by the server, including the cumulative value returned when a request stream closes. Request failures are returned as a gRPC status. When present, the trailing metadata keys `x-greptime-err-code` and `x-greptime-err-retry-hint` carry GreptimeDB's error code and retry classification. Preserve the gRPC status and expose the GreptimeDB metadata rather than replacing them with a generic SDK error. ## Retry and Delivery Semantics Retries must be bounded and observable. A unary request may be retried only when the failure is classified as retryable and the deadline still permits it. Do not retry cancellation or deadline-expiration errors. A lost response does not prove that the server rejected a write. Retrying such a request can insert duplicate rows unless the caller's data model makes the operation idempotent. Document this possibility and return the final error when delivery is ambiguous. Do not transparently retry a partially sent `HandleRequests` stream. The server may already have accepted some requests even though the client has not received the cumulative response. Close the failed stream and report the ambiguity to the caller. Keep Arrow Flight bulk ingestion separate from the `GreptimeDatabase` RPCs. Its batching and partial-acceptance behavior needs its own API contract. Use the existing [GreptimeDB ingester repositories](https://github.com/GreptimeTeam?q=ingester&type=all&language=&sort=) to compare public API conventions, but derive wire behavior from the current Protobuf definition and server contract. --- ## Admin API :::tip Note that all Admin API endpoints in this document listen on Metasrv's `HTTP_PORT`, which defaults to `4000`. ::: The Admin API exposes Metasrv status, cluster controls, and metadata recovery operations over HTTP. It does not provide authentication, and some endpoints change cluster behavior or metadata allocation. Deployments must protect the HTTP port with network-level controls. This page covers the following APIs: - /health - /leader - /heartbeat - /node-lease - /maintenance - /procedure-manager - /recovery - /sequence/table All these APIs are under the parent resource `/admin`. In the following sections, we assume that your metasrv instance is running on localhost port 4000. ## /health HTTP endpoint The `/health` endpoint accepts GET requests and returns `OK` when the HTTP service is running. It does not check whether this Metasrv is the leader or whether external dependencies are available. ### Definition ```bash curl -X GET http://localhost:4000/admin/health ``` ### Examples #### Request ```bash curl -X GET http://localhost:4000/admin/health ``` #### Response ```json OK ``` ## /leader HTTP endpoint The `/leader` endpoint accepts GET HTTP requests and you can use this endpoint to query the leader's addr of your metasrv instance. ### Definition ```bash curl -X GET http://localhost:4000/admin/leader ``` ### Examples #### Request ```bash curl -X GET http://localhost:4000/admin/leader ``` #### Response ```json 127.0.0.1:4000 ``` ## /heartbeat HTTP endpoint The `/heartbeat` endpoint accepts GET HTTP requests and you can use this endpoint to query the heartbeat of all datanodes. You can also query the heartbeat data of the datanode for a specified `addr`, however, specifying `addr` in the path is optional. ### Definition ```bash curl -X GET http://localhost:4000/admin/heartbeat ``` | Query String Parameter | Type | Optional/Required | Definition | |:-----------------------|:-------|:------------------|:--------------------------| | addr | String | Optional | The addr of the datanode. | ### Examples #### Request ```bash curl -X GET 'http://localhost:4000/admin/heartbeat?addr=127.0.0.1:4100' ``` #### Response ```json [ [ { "timestamp_millis": 1677049348651, "id": 1, "addr": "127.0.0.1:4100", "rcus": 0, "wcus": 0, "region_num": 2, "region_stats": [], "topic_stats": [], "node_epoch": 0, "datanode_workloads": { "types": [] }, "gc_stat": null } ] ] ``` ## /node-lease HTTP endpoint The `/node-lease` endpoint returns the current leases recorded for Datanodes. Use it when diagnosing whether Metasrv still considers a Datanode active. ```bash curl -X GET http://localhost:4000/admin/node-lease ``` ## /maintenance HTTP endpoint Maintenance mode temporarily disables automatic cluster management operations during upgrades, planned downtime, or similar work. See [Cluster Maintenance Mode](/user-guide/deployments-administration/maintenance/maintenance-mode.md) for its effect on the cluster. The `/maintenance` endpoint supports the following HTTP requests: - `GET /admin/maintenance` or `GET /admin/maintenance/status`: query the maintenance mode status. - `POST /admin/maintenance/enable`: enable maintenance mode. - `POST /admin/maintenance/disable`: disable maintenance mode. The response body uses the following format: ```json { "enabled": true } ``` ## /procedure-manager HTTP endpoint This endpoint is used to manage the Procedure Manager status. For more details, please refer to [Prevent Metadata Changes](/user-guide/deployments-administration/maintenance/prevent-metadata-changes.md). The `/procedure-manager` endpoint supports the following HTTP requests: - `GET /admin/procedure-manager/status`: query the Procedure Manager status. - `POST /admin/procedure-manager/pause`: pause the Procedure Manager. - `POST /admin/procedure-manager/resume`: resume the Procedure Manager. The response body uses the following format: ```json { "status": "running" } ``` ## /recovery HTTP endpoints Recovery mode gates metadata repair endpoints such as manual table ID sequence changes. It is intended for recovery work, not routine maintenance. - `GET /admin/recovery/status`: query whether recovery mode is enabled. - `POST /admin/recovery/enable`: enable recovery mode. - `POST /admin/recovery/disable`: disable recovery mode. The response body uses the following format: ```json { "enabled": true } ``` Disable recovery mode after the repair is complete. Use [maintenance mode](/user-guide/deployments-administration/maintenance/maintenance-mode.md) instead when the goal is to suspend automatic cluster operations during planned maintenance. ## /sequence/table HTTP endpoints These endpoints inspect or repair the table ID sequence: - `GET /admin/sequence/table/next-id`: return the next table ID without allocating it. - `POST /admin/sequence/table/set-next-id`: advance the next table ID. Setting the sequence requires recovery mode. The new value must be greater than the current value; the API cannot move the sequence backwards. Recovery mode is an API precondition, not a DDL barrier. Follow [Manage table ID sequences](/user-guide/deployments-administration/maintenance/sequence-management.md) for the required cluster-wide procedure. ```bash curl -X POST \ -H 'Content-Type: application/json' \ -d '{"next_table_id": 2048}' \ http://localhost:4000/admin/sequence/table/set-next-id ``` Changing this value affects IDs allocated to future tables. --- ## Metasrv(Metasrv) ## What's in Metasrv Metasrv is the metadata and coordination service in a distributed GreptimeDB cluster. It does not sit on the data path. Its main responsibilities are: - storing Catalog, Schema, Table, Region, route, and node metadata; - choosing Datanodes for new Regions and maintaining table routes; - electing one Metasrv leader to coordinate metadata changes; - running recoverable procedures for DDL, Region migration, failover, and repartitioning; - tracking node leases and Region statistics through heartbeats; - broadcasting cache invalidations to Frontends, Datanodes, and Flownodes when metadata changes; - sending Region lifecycle instructions to Datanodes. ## How the Frontend interacts with Metasrv Frontend obtains table metadata and Region routes from Metasrv and caches them locally. Metadata-changing statements are sent to the Metasrv leader, while reads and writes use the cached routes to reach Datanodes directly. The control and data paths are separate: ```text Frontend |-- metadata lookup and DDL ------------> Metasrv leader `-- Region reads and writes ------------> Datanode Metasrv leader |-- Region lifecycle instructions ------> Datanode `-- cache invalidations ----------------> Frontend / Datanode / Flownode Datanode `-- heartbeat, lease renewal, Region stats -> Metasrv leader ``` In steady state, a table route records one leader peer and zero or more follower peers for each Region. The leader is the write target. Deployments with read-replica support can route reads to followers: ```text Table route |-- Region 0 | |-- leader -> Datanode A | `-- followers -> Datanode B, Datanode C `-- Region 1 `-- leader -> Datanode D ``` Region migration or failover changes peer roles and can temporarily leave a Region without a leader. Frontend refreshes its cached route before sending subsequent reads or writes to the current peers. ### Create Table 1. Frontend submits the DDL request to the Metasrv leader. 2. Metasrv derives Regions from the partition rules and [selects a Datanode for each Region](/contributor-guide/metasrv/selector.md). 3. A persisted procedure creates the Regions and records the table and route metadata. If leadership changes, the procedure can resume from its persisted state. 4. Metasrv notifies Frontends after the metadata change is committed so their caches can be refreshed. ### Insert Frontend resolves the table route, splits rows according to the partition rules, and sends each Region write to the corresponding Datanode. Route changes cause the cached metadata to be invalidated and fetched again from Metasrv. ### Select Frontend uses table and Region metadata while planning the query. Predicates on partition columns prune Regions, and the distributed query engine sends work to the Datanodes that own the selected Regions. See [Distributed Querying](../frontend/distributed-querying.md). ## Metasrv Architecture The main coordination paths are: ```text Leader election | v Metasrv leader ├─ DDL manager -> Procedure manager ├─ Selector -> new Region placement ├─ Heartbeat handler chain -> leases and Region statistics ├─ Region supervisor -> Region migration procedures ├─ Mailbox -> cache invalidations and Region instructions └─ Metadata managers -> KV backend ``` These mechanisms share metadata, but they have different failure boundaries. A process restart may discard caches and leader-local state; metadata and procedure state required for recovery must be durable. ## Distributed Consensus Metasrv separates leader election from metadata storage. Only the elected Metasrv leader performs coordination and metadata-changing operations. Other Metasrv nodes direct clients to the current leader. The key-value backend stores table metadata, routes, procedure state, and other information that must survive a leader change. Metasrv does not use this election to create leader and follower replicas for Datanode Regions; Region availability is managed through heartbeats, Region failure detection, and failover procedures. ## Heartbeat Management Datanodes maintain heartbeat streams to the Metasrv leader. Heartbeat requests report node identity, lease information, Region statistics, and other state used for placement and supervision. Responses carry control messages such as Region lifecycle instructions and cache invalidations. A heartbeat drives two independent mechanisms, and a change to heartbeat timing affects both: - **Node lease.** The keep-lease handler renews the sending Datanode's lease. Selectors and the `/node-lease` endpoint use these leases to decide whether a Datanode is still active. - **Region failure detection.** The Region supervisor keeps a per-Region Phi Accrual detector over heartbeat arrival intervals. Its verdict is independent of lease expiry. A failure verdict submits a failover migration only when Region failover is enabled; it is disabled by default and requires remote WAL unless explicitly allowed on local WAL. Maintenance mode also suppresses failover. See [Region Failover](/user-guide/deployments-administration/manage-data/region-failover.md) for the prerequisites and how to enable it. --- ## Selector ## Introduction When a table is created, Metasrv uses a `Selector` to choose Datanodes for its Regions. Selection uses the current node leases and, depending on the selector, Region statistics. ## Selector Type The `Metasrv` service currently offers the following types of `Selectors`: ### LeaseBasedSelector `LeaseBasedSelector` randomly selects from Datanodes with valid leases. ### LoadBasedSelector The `LoadBasedSelector` load value is determined by the number of regions on each `Datanode`, fewer regions indicate lower load, and `LoadBasedSelector` prioritizes selecting low-load `Datanodes`. ### RoundRobinSelector [default] `RoundRobinSelector` selects `Datanode`s in a round-robin fashion. It is the default and recommended choice for most deployments. ## Configuration You can configure the `Selector` by its name when starting the `Metasrv` service. - LeaseBasedSelector: `lease_based` or `LeaseBased` - LoadBasedSelector: `load_based` or `LoadBased` - RoundRobinSelector: `round_robin` or `RoundRobin` For example: ```shell cargo run -- metasrv start --selector round_robin ``` ```shell cargo run -- metasrv start --selector RoundRobin ``` --- ## Contributor Guide This guide explains the internal design of GreptimeDB for contributors. Start with [Getting Started](/contributor-guide/getting-started.md) to build and run it from source. Submission requirements, including the CLA, license headers, formatting, and the checks a pull request must pass, are maintained in the source repository's [CONTRIBUTING.md](https://github.com/GreptimeTeam/greptimedb/blob/main/CONTRIBUTING.md). ## Architecture For the architecture and components of GreptimeDB, please see the [Architecture](/user-guide/concepts/architecture.md) document in the user guide. For more details on each component, see the following guides: - [frontend][1] - [datanode][2] - [metasrv][3] - [flownode][4] [1]: /contributor-guide/frontend/overview.md [2]: /contributor-guide/datanode/overview.md [3]: /contributor-guide/metasrv/overview.md [4]: /contributor-guide/flownode/overview.md ## Additional reference [DeepWiki](https://deepwiki.com/GreptimeTeam/greptimedb) provides an automatically generated walkthrough of the GreptimeDB repository. It can help when exploring an unfamiliar area, but it is a secondary reference: verify version-sensitive behavior against the source code. --- ## Integration Test ## Introduction Integration tests cover behavior that crosses crate or service boundaries, such as HTTP and gRPC handling, distributed components, or external storage. They use Rust's test harness and live in the [`tests-integration`](https://github.com/GreptimeTeam/greptimedb/tree/main/tests-integration) package. Run the package with: ```shell cargo nextest run -p tests-integration ``` Some cases require environment variables or fixtures for external services. Follow the package's [setup instructions](https://github.com/GreptimeTeam/greptimedb/blob/main/tests-integration/README.md) before running those cases. Use an integration test when a crate-level test or a Sqlness case cannot exercise the required boundary. Keep isolated logic in unit tests so that failures remain fast to reproduce. --- ## Tests Choose the narrowest test that exercises the behavior you changed: | Test type | Use it for | Typical command | | --- | --- | --- | | [Unit test](unit-test.md) | Logic contained within one crate or component | `cargo nextest run -p ` | | [Sqlness test](sqlness-test.md) | SQL, protocol, planner, execution, and end-to-end regressions | `cargo sqlness bare -t ` | | [Integration test](integration-test.md) | Behavior that crosses components or requires external services | `cargo nextest run -p tests-integration` | | [Compatibility test](https://github.com/GreptimeTeam/greptimedb/blob/main/tests/compatibility/README.md) | Reading data or metadata written by an older release | `cargo sqlness compat --from-version ` | Run `make test` when a change needs the full Rust workspace test suite. Changes to persisted metadata, WAL records, SST files, or wire formats should also include a compatibility test when an older release may have produced the input. --- ## Sqlness Test ## Introduction Sqlness is GreptimeDB's end-to-end regression suite for SQL and protocol behavior. A case sends statements to a running GreptimeDB instance and compares the output with a checked-in result file. ## Sqlness manual ### Case file Each case uses two files: - `.sql`: test input, SQL only - `.result`: expected test output, SQL and its results Write the input in the `.sql` file and run the test to generate or update `.result`. Review every result diff: accept it only when the behavior change is intended. ### Case organization Input cases live under `tests/cases`. The first directory level selects an environment. For example, `standalone/` runs against a standalone GreptimeDB instance. Within an environment, group a new case with the feature it exercises. Sqlness discovers case files recursively. ## Run the test Run the suite with: ```shell cargo sqlness bare ``` The command builds and starts GreptimeDB, runs the selected cases, and compares their output. A changed `.result` file is part of the review, not proof that the new output is correct. ### Run a specific test ```shell cargo sqlness bare -t your_test ``` The `-t` or `--test-filter` option accepts a regex string. Sqlness examines case names in the format of `env:case`. --- ## Unit Test ## Introduction Unit tests are embedded into the codebase, usually placed next to the logic being tested. They are written using Rust's `#[test]` attribute. GreptimeDB uses [`cargo-nextest`](https://nexte.st/) as its primary Rust test runner. Install it with: ```shell cargo install cargo-nextest --locked ``` Run the package you changed first: ```shell cargo nextest run -p ``` Use a test name or nextest filter to narrow the run further while developing. Before submitting a change with broad effects, run the full workspace suite: ```shell make test ``` ## Coverage CI reports unit-test coverage. Add tests for changed behavior and failure cases that could otherwise regress; coverage percentage alone is not the goal. --- ## Monitor Kubernetes Metrics with Prometheus and GreptimeDB This guide demonstrates how to set up a complete Kubernetes monitoring solution using Prometheus for metrics collection and GreptimeDB as the long-term storage backend. ## What is Kubernetes Monitoring Kubernetes monitoring is the practice of collecting, analyzing, and acting on metrics and logs from a Kubernetes cluster. It provides visibility into the health, performance, and resource utilization of your containerized applications and infrastructure. Key aspects of Kubernetes monitoring include: - **Resource Metrics**: CPU, memory, disk, and network usage for nodes, pods, and containers - **Cluster Health**: Status of cluster components like kube-apiserver, etcd, and controller-manager - **Application Metrics**: Custom metrics from your applications running in the cluster - **Events and Logs**: Kubernetes events and container logs for troubleshooting Effective monitoring helps you: - Detect and diagnose issues before they impact users - Optimize resource utilization and reduce costs - Plan capacity based on historical trends - Ensure SLA compliance - Troubleshoot performance bottlenecks ## Architecture Overview The monitoring architecture consists of the following components: ![Kubernetes Monitoring Architecture](/k8s-metrics-monitor-architecture.drawio.svg) **Components:** - **kube-state-metrics**: Exports cluster-level metrics about Kubernetes objects (deployments, pods, services, etc.) - **Node Exporter**: Exports hardware and OS-level metrics from each Kubernetes node - **Prometheus Operator**: Automates Prometheus deployment and configuration using Kubernetes custom resources - **GreptimeDB**: Acts as the long-term storage backend for Prometheus metrics with high compression and query performance - **Grafana**: Provides dashboards and visualizations for metrics stored in GreptimeDB ## Prerequisites Before starting, ensure you have: - A running Kubernetes cluster (version >= 1.18) - `kubectl` configured to access your cluster - [Helm](https://helm.sh/docs/intro/install/) v3.0.0 or higher installed - Sufficient cluster resources (at least 2 CPU cores and 4GB memory available) ## Install GreptimeDB GreptimeDB serves as the long-term storage backend for Prometheus metrics. For detailed installation steps, please refer to the [Deploy GreptimeDB Cluster](/user-guide/deployments-administration/deploy-on-kubernetes/deploy-greptimedb-cluster.md) documentation. ### Verify the GreptimeDB Installation After deploying GreptimeDB, verify that the cluster is running. In this guide we assume the GreptimeDB cluster is deployed in the `greptime-cluster` namespace and named `greptimedb`. ```bash kubectl -n greptime-cluster get greptimedbclusters.greptime.io greptimedb ``` ```bash NAME FRONTEND DATANODE META FLOWNODE PHASE VERSION AGE greptimedb 1 2 1 1 Running v1.0.1 33s ``` Check the pods: ```bash kubectl get pods -n greptime-cluster ``` ```bash NAME READY STATUS RESTARTS AGE greptimedb-datanode-0 1/1 Running 0 71s greptimedb-datanode-1 1/1 Running 0 97s greptimedb-flownode-0 1/1 Running 0 64s greptimedb-frontend-8bf9f558c-7wdmk 1/1 Running 0 90s greptimedb-meta-fc4ddb78b-nv944 1/1 Running 0 87s ``` ### Access GreptimeDB To interact with GreptimeDB directly, you can port-forward the frontend service to your local machine. GreptimeDB supports multiple protocols, with MySQL protocol available on port `4002` by default. ```bash kubectl port-forward -n greptime-cluster svc/greptimedb-frontend 4002:4002 ``` Connect using any MySQL-compatible client: ```bash mysql -h 127.0.0.1 -P 4002 ``` ### Storage Partitioning To improve query performance and reduce storage costs, GreptimeDB automatically creates columns based on Prometheus metric labels and stores metrics in a physical table. The default table name is `greptime_physical_table`. Since we deployed a GreptimeDB cluster with [multiple datanodes](#verify-the-greptimedb-installation), you can partition the table to distribute data across datanodes for better scalability and performance. In this Kubernetes monitoring scenario, we can use the `namespace` label as the partition key. For example, with namespaces like `kube-public`, `kube-system`, `monitoring`, `default`, `greptime-cluster`, and `etcd-cluster`, you can create a partitioning scheme based on the first letter of the namespace: ```sql CREATE TABLE greptime_physical_table ( greptime_value DOUBLE NULL, namespace STRING PRIMARY KEY, greptime_timestamp TIMESTAMP TIME INDEX, ) PARTITION ON COLUMNS (namespace) ( namespace < 'f', namespace >= 'f' AND namespace < 'g', namespace >= 'g' AND namespace < 'k', namespace >= 'k' ) ENGINE = metric WITH ( "physical_metric_table" = "" ); ``` For more information about Prometheus metrics storage and query performance optimization, refer to the [Improve efficiency by using metric engine](/user-guide/ingest-data/for-observability/prometheus.md#improve-efficiency-by-using-metric-engine) guide. ### Prometheus URLs in GreptimeDB GreptimeDB provides [Prometheus-compatible APIs](/user-guide/query-data/promql.md#prometheus-http-api) under the HTTP context `/v1/prometheus/`, enabling seamless integration with existing Prometheus workflows. To integrate Prometheus with GreptimeDB, you need the GreptimeDB service address. Since GreptimeDB runs inside the Kubernetes cluster, use the internal cluster address. The GreptimeDB frontend service address follows this pattern: ``` -frontend..svc.cluster.local: ``` In this guide: - GreptimeDB cluster name: `greptimedb` - Namespace: `greptime-cluster` - Frontend port: `4000` So the service address is: ```bash greptimedb-frontend.greptime-cluster.svc.cluster.local:4000 ``` The complete [Remote Write URL](/user-guide/ingest-data/for-observability/prometheus.md#remote-write-configuration) for Prometheus is: ```bash http://greptimedb-frontend.greptime-cluster.svc.cluster.local:4000/v1/prometheus/write ``` This URL consists of: - **Service endpoint**: `greptimedb-frontend.greptime-cluster.svc.cluster.local:4000` - **API path**: `/v1/prometheus/write` ## Install Prometheus Now that GreptimeDB is running, we'll install Prometheus to collect metrics and send them to GreptimeDB for long-term storage. ### Add the Prometheus Community Helm Repository ```bash helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update ``` ### Install the kube-prometheus-stack The [`kube-prometheus-stack`](https://github.com/prometheus-operator/kube-prometheus) is a comprehensive monitoring solution that includes Prometheus, Grafana, kube-state-metrics, and node-exporter components. This stack automatically discovers and monitors all Kubernetes namespaces, collecting metrics from cluster components, nodes, and workloads. In this deployment, we'll configure Prometheus to use GreptimeDB as the remote write destination for long-term metric storage and configure Grafana's default Prometheus data source to use GreptimeDB. Create a `kube-prometheus-values.yaml` file with the following configuration: ```yaml # Configure Prometheus remote write to GreptimeDB prometheus: prometheusSpec: remoteWrite: - url: http://greptimedb-frontend.greptime-cluster.svc.cluster.local:4000/v1/prometheus/write # Configure Grafana to use GreptimeDB as the default Prometheus data source grafana: datasources: datasources.yaml: apiVersion: 1 datasources: - name: Prometheus type: prometheus url: http://greptimedb-frontend.greptime-cluster.svc.cluster.local:4000/v1/prometheus access: proxy editable: true ``` This configuration file specifies [the GreptimeDB service address](#prometheus-urls-in-greptimedb) for: - **Prometheus remote write**: Sends collected metrics to GreptimeDB for long-term storage - **Grafana data source**: Configures GreptimeDB as the default Prometheus data source for dashboard queries Install the `kube-prometheus-stack` using Helm with the custom values file: ```bash helm install kube-prometheus prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --values kube-prometheus-values.yaml ``` ### Verify the Installation Check that all Prometheus components are running: ```bash kubectl get pods -n monitoring ``` ```bash NAME READY STATUS RESTARTS AGE alertmanager-kube-prometheus-kube-prome-alertmanager-0 2/2 Running 0 60s kube-prometheus-grafana-78ccf96696-sghx4 3/3 Running 0 78s kube-prometheus-kube-prome-operator-775fdbfd75-w88n7 1/1 Running 0 78s kube-prometheus-kube-state-metrics-5bd5747f46-d2sxs 1/1 Running 0 78s kube-prometheus-prometheus-node-exporter-ts9nn 1/1 Running 0 78s prometheus-kube-prometheus-kube-prome-prometheus-0 2/2 Running 0 60s ``` ### Verify the Monitoring Status Use [MySQL protocol](#access-greptimedb) to query GreptimeDB and verify that Prometheus metrics are being written. ```sql SHOW TABLES; ``` You should see tables created for various Prometheus metrics. ```sql +---------------------------------------------------------------------------------+ | Tables | +---------------------------------------------------------------------------------+ | :node_memory_MemAvailable_bytes:sum | | ALERTS | | ALERTS_FOR_STATE | | aggregator_discovery_aggregation_count_total | | aggregator_unavailable_apiservice | | alertmanager_alerts | | alertmanager_alerts_invalid_total | | alertmanager_alerts_received_total | | alertmanager_build_info | | ...... | +---------------------------------------------------------------------------------+ 1553 rows in set (0.18 sec) ``` ## Use Grafana for Visualization Grafana is included in the kube-prometheus-stack and comes pre-configured with dashboards for comprehensive Kubernetes monitoring. ### Access Grafana Port-forward the Grafana service to access the web interface: ```bash kubectl port-forward -n monitoring svc/kube-prometheus-grafana 3000:80 ``` ### Get Admin Credentials Retrieve the admin password using kubectl: ```bash kubectl get secret --namespace monitoring kube-prometheus-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo ``` ### Login Grafana 1. Open your browser and navigate to [http://localhost:3000](http://localhost:3000) 2. Login with: - **Username**: `admin` - **Password**: The password retrieved from the previous step ### Explore Pre-configured Dashboards After logging in, navigate to **Dashboards** to explore the pre-configured Kubernetes monitoring dashboards: - **Kubernetes / Compute Resources / Cluster**: Overview of cluster-wide resource utilization - **Kubernetes / Compute Resources / Namespace (Pods)**: Resource usage breakdown by namespace - **Kubernetes / Compute Resources / Node (Pods)**: Node-level resource monitoring - **Node Exporter / Nodes**: Detailed node hardware and OS metrics ![Grafana Dashboard](/k8s-prom-monitor-grafana.jpg) ## Conclusion You now have a complete Kubernetes monitoring solution with Prometheus collecting metrics and GreptimeDB providing efficient long-term storage. This setup enables you to: - Monitor cluster and application health in real-time - Store metrics for historical analysis and capacity planning - Create rich visualizations and dashboards with Grafana - Query metrics using both PromQL and SQL For more information about GreptimeDB and Prometheus integration, see: - [Prometheus Integration](/user-guide/ingest-data/for-observability/prometheus.md) - [Query Data in GreptimeDB](/user-guide/query-data/overview.md)