I tried out TiDB Cloud Lake in public preview

I tried out TiDB Cloud Lake in public preview

TiDB Cloud has released "TiDB Cloud Lake," a data warehouse service for analytical workloads, in public preview. We will introduce the results of actually trying it out, including its features such as the ability to read S3 files directly with SQL without loading them, and the mechanism by which it runs on a stack independent from TiDB Cloud clusters.
2026.09.15

This page has been translated by machine translation. View original

Hello, I'm sora from the Game Solutions Division.
This time, I'll write about my experience trying out TiDB Cloud Lake, which started its public preview on June 30, 2026.

I was particularly interested in the ability to query files stored on S3 with SQL without loading them, and the fact that it runs on a completely separate stack from TiDB Cloud clusters, so I focused my investigation on those aspects.

Please note that the content of this article reflects the state as of September 15, 2026, and may change by GA since it is currently in public preview.

What is TiDB Cloud Lake

It is a data warehouse service for analytical workloads.
https://docs.pingcap.com/tidbcloudlake/lake-overview/

It has a compute-storage separated architecture, with compute units called Warehouses.
You can start them only when needed and stop them when not in use.

Regarding data storage, the pricing page states "physically stored in Amazon S3."
Storage pricing is also based on S3 prices.

In addition to SQL-based analytics, full-text search, vector search, and geospatial analysis are all included in the same engine.
You can also query files on S3 directly without loading them into tables.

Relationship with TiDB Cloud Clusters

This was what I was most curious about.
Since TiDB has TiKV (row store) and TiFlash (column store), it was unclear how TiDB Cloud Lake would fit in with them.

After trying it out, TiDB Cloud Lake is not a service designed to be used in conjunction with TiDB Cloud clusters.
You can create TiDB Cloud Lake on its own without owning any clusters, and the console entry point is also separate.
My understanding is that the only connection is that they share the same TiDB Cloud account and billing.

Running on a Separate Stack

TiDB Cloud Lake uses a different engine called FuseEngine for its storage layer.
Its metadata also has an independent Raft cluster, unrelated to the cluster-side PD.

The cluster side also uses an architecture called TiDB X, which uses object storage as its persistence layer.
However, that configuration has TiKV-equivalent and TiFlash-equivalent components running as cache layers on top of it, making it distinct from TiDB Cloud Lake.
https://docs.pingcap.com/tidbcloud/tidb-x-architecture/

Data ingestion from clusters into TiDB Cloud Lake is provided via MySQL Integration Task (snapshot + CDC) or S3-based loading.

Comparison with TiFlash

Item TiFlash (HTAP) TiDB Cloud Lake
Data location Inside TiDB cluster, synced from TiKV (row store) Separate storage on TiDB Cloud Lake side, requires separate ingestion
Target data Only data stored in TiDB S3 logs, Kafka, other DBs, Iceberg can be mixed

I think TiDB Cloud Lake is the right choice when you want to mix and analyze data from outside the cluster, such as S3 logs, or when you want to aggregate large amounts of data accumulated over a long period.

Trying It Out

Creating TiDB Cloud Lake

From the TiDB Cloud console's left navigation, go to "My Lake" and click "Try TiDB Cloud Lake" to enter the creation screen.

The post-creation console is also independent and does not navigate back and forth with the cluster-side screen.
Only the billing screen is not on the TiDB Cloud Lake side; you view it through TiDB Cloud's Billing.

01-sr-my-lake

In the creation dialog, you specify the name, plan, cloud, and region.

02-sr-create-default

The cloud and region cannot be changed after creation, so you need to be careful here.

The following cloud providers and regions are currently supported, and the selected cloud platforms and regions cannot be modified after successful creation.

The available clouds are AWS and Alibaba Cloud, and AWS offered 5 regions including Tokyo (ap-northeast-1).
This time I created it with the Personal plan in the AWS Tokyo region.
03-sr-create-filled

After pressing Create, initialization begins, and based on the Audit Logs timestamps, it completed in about 2 minutes.

State Right After Creation

After creation was complete, 8 tutorials were loaded into the Worksheet.
04-sr-worksheets

There are 8 tutorials covering user creation, Stage and loading, analytics, JSON search, vector search, geospatial, Stream, and ETL — an overview of what this product can do.
From what I could see, they were in a pre-execution state.

A Warehouse named default is also automatically created.
The size was Small, Auto Suspend was 5 minutes, and the status was Suspended.
05-sr-warehouses

Since it is created in a stopped state, there is no billing at this point.

Warehouse Settings

Since the auto-generated default was Small ($3.20/h), I tried changing it to XSmall ($1.60/h).
Opening Setting from the menu under Admin > Warehouses allows you to change the size without recreating it.
06-sr-wh-settings

Auto Suspend was in a dropdown selection format.
07-sr-auto-suspend

There are 7 options: 1 min / 5 min / 10 min / 15 min / 30 min / 1 hour / Never, with the default being 5 minutes.
Selecting Never disables automatic suspension.

I also tried deleting it, and after doing so, I recreated a test-warehouse at XSmall for verification.
When creating a new one, it starts Running immediately upon creation, and billing begins from that point.
It took less than 10 seconds to start.
08-sr-wh-running

The Warehouse detail screen has an item called Auto Resume: True, and the setting that automatically starts the Warehouse when a query arrives while it is stopped was enabled by default.
In fact, when I later executed SQL, the suspended Warehouse automatically switched to Running.

Some Items Are Disabled

3 items under Advanced Options were grayed out with "Want to Enable it? Contact us" displayed.

Item Description Official Documentation Note
Role Specifying the role tied to the Warehouse (fixed to public) Disabled by default. Requires submitting a support ticket
Multi-cluster Warehouse Automatic adjustment of cluster count based on demand Disabled by default. Requires application, and only available for Business and Dedicated
Enable MySQL Endpoint Endpoint for BI tools that only support MySQL protocol Disabled by default. Requires submitting a support ticket

https://docs.pingcap.com/tidbcloudlake/warehouse/

For Role and MySQL Endpoint, there is no mention of restrictions by Edition; both simply state "disabled by default, please submit a request."
Only Multi-cluster Warehouse is listed as Business or higher in the Edition feature comparison.

https://docs.pingcap.com/tidbcloudlake/editions/

Incidentally, creating, deleting, and resizing Warehouses is under Admin, and cannot be done from Data or Projects.

Connecting to S3 with an IAM Role

To read S3 data, you register connection information.
There are two methods: using access keys and using an IAM role, so I tried the role method.

You need the TiDB-side ARN to write in the trust policy, but the official documentation states:

https://docs.pingcap.com/tidbcloudlake/authenticate-with-aws-iam-role/

Raise a support ticket to get the IAM role ARN for your TiDB Cloud Lake organization

I thought a support ticket would be required, but it was actually displayed on screen when I selected AWS – SQS(S3) under Data > Data Sources > Create.
09-sr-platform-role

Selecting AWS – Credentials only shows input fields for Access Key / Secret Key, so it was just hard to find.
The External ID is the same value as the Lake Profile ID.

The setup role displayed here is named arn:aws:iam::xxxxxxxxxxxx:role/databend-webapi, which connects to the storage engine discussion mentioned later.

I limited the permission policy to just reading and writing to the bucket.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetBucketLocation"
      ],
      "Resource": "arn:aws:s3:::<bucket name>"
    },
    {
      "Sid": "ReadWriteObjects",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::<bucket name>/tidb-cloud-lake/*"
    }
  ]
}

The reason s3:GetBucketLocation is included is that TiDB Cloud Lake automatically detects the region.
Since it falls back to us-east-1 if detection fails, I explicitly allowed it.

In the trust policy, I set the two Platform roles shown in the console and the External ID.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::xxxxxxxxxxxx:role/databend-webapi",
          "arn:aws:iam::xxxxxxxxxxxx:role/mars/tnxxxxxxx/mars-tnxxxxxxx"
        ]
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<Lake ID>"
        }
      }
    }
  ]
}

Creating a CONNECTION and Stage

Execute the following in the Worksheet.

CREATE CONNECTION lake_s3_conn
    STORAGE_TYPE = 's3'
    ROLE_ARN     = 'arn:aws:iam::<AWS Account ID>:role/tidb-cloud-lake-role'
    EXTERNAL_ID  = '<Lake ID>';

CREATE STAGE orders_stage
    URL = 's3://<bucket name>/tidb-cloud-lake/'
    CONNECTION = (CONNECTION_NAME = 'lake_s3_conn');

LIST @orders_stage;

If you can see files with LIST, the AssumeRole was successful.
10-sr-list-stage

orders.csv was 5,573,478 bytes and orders.parquet was 2,596,474 bytes, matching the uploaded files.
md5 is also returned, so it can be used to verify integrity.

Exporting the results of SHOW STAGES allows you to check the configuration held by the Stage.

{
  "access_key_id": "",
  "secret_access_key": "",
  "security_token": "",
  "role_arn": "arn:aws:iam::<AWS Account ID>:role/tidb-cloud-lake-role",
  "external_id": "la***xl",
  "region": "ap-northeast-1",
  "storage_class": "Standard"
}

The credentials fields are empty, confirming that no keys are stored at all.
The region was also correctly detected without being explicitly specified.
The External ID is masked, while the role_arn is retained as-is.

Querying S3 Files Without Loading Them

This is what I felt was most characteristic of TiDB Cloud Lake.
You can directly query files on S3 with SQL without creating a table.

Since the CSV has a header row, I first create a named file format.
This is because SKIP_HEADER cannot be written inline.

CREATE FILE FORMAT csv_with_header
    TYPE = CSV,
    FIELD_DELIMITER = ',',
    RECORD_DELIMITER = '\n',
    COMPRESSION = AUTO,
    SKIP_HEADER = 1;

SELECT $1, $2, $3, $4, $5, $6, $7
FROM @orders_stage (
    FILE_FORMAT => 'csv_with_header',
    PATTERN     => '.*[.]csv'
)
LIMIT 10;

11-sr-query-csv

Columns are accessed by positional reference using $1 through $7.
On the right side of the results pane, Query Details is shown, and the Scan Size was 5.32MB.
Even with LIMIT 10, it reads the entire CSV file.

All types are treated as String.
This is expected since CSV has no type information, but casting is required for aggregation.

Parquet could be read with just the built-in specification.

SELECT *
FROM @orders_stage (
    FILE_FORMAT => 'parquet',
    PATTERN     => '.*[.]parquet'
)
LIMIT 10;

12-sr-query-parquet

Since column names and types are embedded in the file, neither positional references like $1 nor casting are necessary.
order_id and quantity are recognized as Int64, amount as Float64, and ordered_at as Timestamp.

The column profile in the results pane also changes based on type — a histogram for numbers and a frequency table for strings.

13-sr-col-profile

Loading into a Table and Aggregating

Next, I tried loading data into a table.

USE default;

CREATE TABLE orders (
    order_id     BIGINT,
    customer_id  BIGINT,
    category     VARCHAR,
    channel      VARCHAR,
    amount       DECIMAL(12, 2),
    quantity     INT,
    ordered_at   TIMESTAMP
);

COPY INTO orders FROM @orders_stage
PATTERN = '.*[.]csv'
FILE_FORMAT = (TYPE = 'CSV', SKIP_HEADER = 1);

14-sr-copy-into

100,000 rows were loaded in 650ms.
The result returns not only Rows_loaded but also Errors_seen, First_error, and First_error_line, so you can track what happened and at which line in case of failure.

COUNT(*) Returns Without Reading Data

After loading, I counted the rows.

SELECT COUNT(*) FROM orders;

15-sr-count

The result was 100000, with a Query Duration of 9ms and a Scan Size of 1B.
It returns the answer from table statistics without reading the data.
Doing the same thing with a direct Stage query scanned 5.32MB, so for repeated queries, loading the data first is faster.

Types Are Converted According to the Definition

DESC orders;

16-sr-desc

amount was stored as DECIMAL(12, 2) and ordered_at as TIMESTAMP.
Since all columns were String in direct Stage queries, loading the data interprets them using the DDL types.

SELECT * FROM orders LIMIT 5;
order_id,customer_id,category,channel,amount,quantity,ordered_at
75310,1097,books,web,54246.02,2,2026-01-10 18:15:00.000000
75311,1383,grocery,ios,35545.20,3,2026-07-11 22:34:00.000000
75312,4132,apparel,web,47361.31,5,2026-04-07 10:14:00.000000

Incidentally, LIMIT 5 did not return results starting from order_id = 1.
Since it is distributed storage, row order is not guaranteed, and ORDER BY is needed if you want results from the beginning.

I also tried aggregation.

SELECT
    category,
    COUNT(*)    AS order_count,
    SUM(amount) AS total_amount,
    AVG(amount) AS avg_amount
FROM orders
GROUP BY category
ORDER BY total_amount DESC;

17-sr-group-by

Each of the 5 categories had approximately 20,000 records, totaling exactly 100,000.
Since the dummy data was distributed uniformly across categories, all values are nearly identical.

Internal Implementation Visible in CREATE Statement

Selecting a table under Data > Databases displays the CREATE statement in the Table Definition tab.

18-sr-table-def

CREATE TABLE orders (
    order_id BIGINT NULL,
    customer_id BIGINT NULL,
    category VARCHAR NULL,
    channel VARCHAR NULL,
    amount DECIMAL(12, 2) NULL,
    quantity INT NULL,
    ordered_at TIMESTAMP NULL
) ENGINE=FUSE
  COMPRESSION='zstd'
  ENABLE_AUTO_ANALYZE='1'
  SNAPSHOT_LOCATION='4361753/4364629/_ss/h01a0a3a9bd1f76acb1bca864404e2038_v4.mpk'
  STORAGE_FORMAT='parquet'

Options that weren't in the DDL I wrote have been filled in automatically.

Option What It Tells Us
ENGINE=FUSE The storage engine is FuseEngine
STORAGE_FORMAT='parquet' The internal storage format is also Parquet
COMPRESSION='zstd' zstd compression. 100,000 rows at 1.25MB (about 24% of the original CSV)
ENABLE_AUTO_ANALYZE='1' Automatic statistics updates are ON by default
SNAPSHOT_LOCATION The substance of time travel. Snapshot generation management

Combining the name FuseEngine with the Platform role name databend-webapi mentioned earlier, you can infer that the base of TiDB Cloud Lake is Databend.
Despite carrying the TiDB name, this CREATE statement reveals that it is a completely different entity from TiKV or TiFlash at the storage engine level.

Writing Back to S3

I tried outputting aggregation results to S3 as Parquet.

I first wrote it as follows and it failed.

COPY INTO 's3://<bucket name>/tidb-cloud-lake/export/category_summary/'
FROM ( SELECT ... )
CONNECTION = (CONNECTION_NAME = 'lake_s3_conn')
FILE_FORMAT = (TYPE = 'PARQUET');
sql_syntax(1005): Unexpected "CONNECTION", a reserved keyword;
use a different identifier or quote it with backticks.

Looking at the official syntax, the unload side does not have a CONNECTION clause.

https://docs.pingcap.com/tidbcloudlake/unload-data/

You can write it for loading (COPY INTO <table> FROM <location>), but not for unloading (COPY INTO <location> FROM <table>).
Using a Stage as the output destination avoids the need to write credentials.

COPY INTO @orders_stage/export/category_summary/
FROM (
    SELECT category, COUNT(*) AS order_count, SUM(amount) AS total_amount
    FROM orders
    GROUP BY category
)
FILE_FORMAT = (TYPE = PARQUET)
DETAILED_OUTPUT = true;

19-sr-unload

1 Parquet file of 5 rows and 1,060 bytes was output.
The filename follows the format data_<Query ID>_0000_00000000.parquet, and judging from the trailing sequential number, larger data would be split into multiple files.

Only the s3:PutObject permission set initially was sufficient, and no multipart upload-related permissions were needed.

Creating a Dashboard

You can create dashboards under Projects > Dashboards.
Pressing Create a Tile opens a tile-specific query editor, where you write a query and then switch to a chart display.

20-sr-dashboard

There are 4 chart types: Scorecard / Pie chart / Bar chart / Line chart.

What caught my attention here is that the Dashboard screen has a Refresh button and a Warehouse selector at the top.
This means it is not a saved image — the query is re-executed on the Warehouse each time you open it.

Since the documentation explicitly states that enabling the MySQL Endpoint disables Auto Suspend, compared to a BI tool with a persistent connection, the built-in Dashboard appears to be more cost-effective since billing only occurs when it is being viewed.

https://docs.pingcap.com/tidbcloudlake/warehouse/

Data Integrations

Data > Data Integrations is a screen for configuring ingestion through a GUI.

21-sr-integration

It is a 3-step wizard where you specify the data source, file path, file format, delimiter, and whether headers are present, allowing you to configure what you did with COPY INTO without any SQL.
The difference is the Continuous Ingestion toggle; turning it On enables continuous ingestion.
Selecting MySQL or PostgreSQL enables continuous sync via snapshot + CDC, so this is likely the place to use for continuously streaming data from other databases into TiDB Cloud Lake.

Connection information is registered in Data > Data Sources.
The available types were: AWS – Credentials / AWS – SQS(S3) / TiDB / MySQL / PostgreSQL / Kafka / FeiShu Bot — 7 in total.

Task

Data > Task is a screen for organizing SQL with dependencies and scheduling periodic execution.

22-sr-task

Schedules can be specified as fixed intervals or using cron expressions.
You can specify other tasks and Streams in Depends, and a Stream is a virtual table that continuously accumulates change differences.
Combining them allows you to build pipelines that "process only newly ingested data and write it to another table."

Monitoring

Under Monitoring > Metrics, you can see storage size, scan volume, query success rate, and Warehouse status all in one place.

23-sr-metrics

Looking at the Warehouse Status graph, you can see the repeated cycles of starting and stopping.
The Data Storage Size is a Lake-wide value, and with just 100,000 rows loaded, it was 1.49MB.

Executed queries are retained under Monitoring > SQL History.

24-sr-sql-history

Success/failure status, execution time, queue wait time, and which Warehouse it ran on are all listed.
The User Agent includes the Worksheet ID as TiDBCloud/worksheet=3461, so you can also track which screen it was executed from.
Incidentally, looking at the history of CREATE CONNECTION, the External ID is masked as la***xl, so sensitive information is not retained as-is.

Under Monitoring > Alerts, you can configure notifications.

25-sr-alerts

There are 3 types of notifications you can subscribe to: TooManyTaskErrors / WarehouseQueuedQueriesHigh / TaskExecutionFailed, covering task failures and Warehouse queue backlogs.
Notification destinations are Email and Feishu Bot.

Monitoring > Audit Logs retains operational history.
Worksheet edits and configuration changes were recorded along with the executor, IP address, and timestamp.

Note: Permission Required to Export Results

When I tried to download query results as CSV, I got a permission error.

26-sr-export-error

Permission 'EXPORT' verification failed
Please contact owner ... to add the corresponding permission.

I was momentarily confused since I was the Lake Owner and it told me to "contact the owner," but there was an Add EXPORT Permission menu item under the in Admin > Roles and Permissions.

27-sr-export-perm

Allows downloading worksheet results

Since a DWH contains customer data, I believe this is a design choice to separate the ability to download results locally as a distinct permission.

Pricing

Warehouses are billed per second, with unit prices determined by size.
There are 2 Editions, and the Business unit price is 1.5x that of Personal.

Size Personal Unit Price
XSmall $1.60/h
Small $3.20/h
Medium $6.40/h
Large $12.80/h
XLarge $25.60/h

Above XLarge, sizes go from 2XLarge to 6XLarge, doubling with each step up.
Unit prices for all sizes are listed below.

https://docs.pingcap.com/tidbcloudlake/pricing-billing/

Storage is $23.00/month/TB, shared across both Editions.
As mentioned earlier, the actual data is stored on S3, so S3 pricing is directly reflected.
There is also one more charge: a REST API request fee for each SQL query execution, at $1.00/10,000 requests for Personal.

Please note that these are prices as of the public preview, and the official documentation explicitly states they may change at GA.

Which to Use

TiFlash is included in cluster resources, so costs are incurred as long as the cluster is running.
On the other hand, TiDB Cloud Lake costs nothing when the Warehouse is stopped, and using XSmall for just 1 hour a day would be about $48 per month.

The distinction seems to be: use the cluster side if you need continuous data access, and use TiDB Cloud Lake if you need to work with large amounts of data only occasionally.

However, since the Warehouse starts up with each query and then stops after 5 minutes of idle time, when verifying while actively working, the actual running time will be longer than you might expect.
Usage charges can be confirmed in the Billing section on the TiDB Cloud side.

Closing

This time, I tried out TiDB Cloud Lake in public preview, exploring things like directly querying S3 files with SQL and loading data into tables for aggregation.
I hope this article is helpful to someone.


TiDB Cloudの導入・サポートはクラスメソッドにお任せください

クラスメソッドでは、TiDB Cloudの導入から運用支援まで、豊富なノウハウでお客様をサポートしています。パフォーマンスの最適化やスケーラビリティに課題を抱えている方は、ぜひご相談ください。
詳細な導入事例やサービス内容について知りたい方は、こちらからご確認いただけます。

TiDB Cloudのサポート詳細を見る

Share this article

AWSのお困り事はクラスメソッドへ