Direct Lake vs. DirectQuery on Databricks: I Measured It

A report is only actionable if it keeps up with the question. When a planner clicks “Winter”, then “Households”, then “2027”, every click is a follow-up thought. At one second per click, people keep asking. At five seconds, they export to Excel.

Many teams keep their data in Azure Databricks and report on it in Power BI. There are four ways to get from one to the other:

  • DirectQuery: the semantic model sends SQL to a Databricks SQL warehouse for every visual, on every click.
  • Direct Lake on the mirrored catalog: Fabric mirrors the Unity Catalog as metadata only. The Delta files stay in Databricks storage, and Power BI loads the columns it needs into memory and answers from there.
  • Direct Lake through Lakehouse shortcuts: a Fabric Lakehouse with OneLake shortcuts to the mirrored tables. Same files, but you pick the tables and can combine them with other Fabric data.
  • Import: the classic. The model keeps its own compressed copy and refreshes it on a schedule.
Four ways from Power BI to Databricks data

I wanted to know how big the speed difference between DirectQuery and Direct Lake really is. So I built both, side by side, on the same data.

The setup

  • Data: a real Power BI report with quarter-hourly values, 420,480 rows for 2026 and 2027. For volume, a clearly labelled synthetic version on top of the real data with 10 million and 100 million rows.
  • Databricks: Unity Catalog, managed Delta tables, a serverless SQL warehouse (Small) with Photon.
  • Fabric: the catalog mirrored into Fabric. Two semantic models per data set with identical tables, relationships and DAX: one in Direct Lake (no fallback allowed), one in DirectQuery with the Azure Databricks connector.
  • Measurement: the same report page with five visuals. I recorded the clicks in the browser, and I sent the page’s queries through the Power BI REST API, warm, cold, and with several users at once.

Same report, same clicks

First the experience a user actually has. Same report, same clicks, recorded on both models. Each clip starts at the same action on both sides; the faster side stops once its page has finished drawing.

Open the report: Direct Lake 6.9 s, DirectQuery 13.2 s
Click Season = Winter: Direct Lake 0.8 s, DirectQuery 4.9 s

Every filter click took 0.7 to 0.8 seconds in Direct Lake and 2.8 to 4.9 seconds in DirectQuery. Opening the report took 6.9 against 13.2 seconds, Fabric portal included.

A Databricks engineer would ask the right question first: where was the warehouse? Mine ran in West Europe and the Fabric capacity in West US 3, so every DirectQuery statement crossed the Atlantic. I deployed the same DirectQuery model on a capacity in Sweden Central, a short hop from the warehouse. It got 9 percent faster, and that on a slightly larger capacity, so distance alone is worth at most 9 percent. The gap stayed at 3x. Distance is not the story.

More data

Real data is small. So I generated 10 million and 100 million rows and measured the full page, five visuals in parallel, like a report loads them.

One user, warm

With one user, Direct Lake loaded the page in 1.1 and 1.6 seconds, DirectQuery in 3.4 and 4.1. In the browser at 10 million rows it looks like this:

10 million rows, click Year = 2027: Direct Lake 0.7 s, DirectQuery 3.9 s

More users

Reports are rarely used by one person at a time. Here is the same page loaded by 1, 5 and 10 users at once:

Several users at once

At 10 million rows, ten simultaneous users barely moved Direct Lake (1.4 seconds). DirectQuery doubled to 7.0. At 100 million rows, ten users got 4.0 seconds in Direct Lake and 8.7 in DirectQuery.

To see it, I opened the 10-million-row report in four browser windows per side and clicked “2027” in all four at the same moment:

Four users click Year = 2027 at once: Direct Lake 2.2 to 2.4 s, DirectQuery 9.6 to 14.6 s

Every Direct Lake window was done after 2.2 to 2.4 seconds. The DirectQuery windows needed 9.6 to 14.6.

The first user of the day

Every in-memory engine has a cold start. Direct Lake is cold after a full reload. DirectQuery is cold every time the warehouse has auto-stopped, by default after ten idle minutes. So what does the first person in the morning get?

What the first user sees

The fix on the Direct Lake side is cheap: one warm-up page at the end of the refresh pipeline. After that, the first user gets 1.7 seconds. DirectQuery with the warehouse kept running still needs about 4 seconds, and after an auto-stop about 13 seconds or more. (Without any warm-up, both start cold: Direct Lake at 10 to 17 seconds, DirectQuery at 13 to 24. That is the one situation where they are close, and the one nobody should leave their users in.)

In the browser, with the Fabric portal and the warehouse start included, the difference is even larger: the reloaded and warmed Direct Lake report was ready after 9.0 seconds, the DirectQuery report after an auto-stop after 44.4.

First user of the day: Direct Lake 9.0 s, DirectQuery 44.4 s

Plan the memory

Direct Lake answers from memory, so the largest table has to fit the capacity. At 100 million rows, my first table layout (one file, four decimals) needed about 8 GB for a single query, more than the 5 GB per-query limit I saw. A simple optimization (16 files, values in watt hours) brought the same data onto F32 and F64. One later run on F64 hit the limit again, so leave some headroom and test at your real volume. For the clips I used 10 million rows.

An independent benchmark points the same way: Modern Power BI Architecture Choices for Reporting on Azure Databricks, written by Liping Huang (Data Leaps) and published in Microsoft’s FabricCAT repository. There, Direct Lake over mirrored Unity Catalog tables slowed down at hundreds of millions to billions of rows, while Direct Lake on Delta tables written in Fabric was the strongest pattern overall in warm and hot cache. The paper also notes that optimizations for the mirrored path are on the way.

So size it up front: the size of the largest fact table, the cardinality of its columns, and the per-query memory limit of your capacity.

Shortcuts work too

The mirrored catalog is not the only way in. I also built a Fabric Lakehouse with OneLake shortcuts to the mirrored tables, in a second workspace on a capacity in another region, and put Direct Lake models on top. Direct Lake ran on it the same way; I measured only its cold load, not warm speed. It is the route to take when you want to choose tables, mix them with Fabric-native data, or place the model in another workspace.

One rule: shortcut the mirrored tables, not the Databricks storage account. Databricks does not support reading managed Unity Catalog tables by storage path, because that bypasses Unity Catalog permissions. A direct ADLS shortcut is only an option for external tables.

And security?

Security is no reason to stay on DirectQuery. With DirectQuery, Databricks checks the Unity Catalog permissions of every user on every query. A mirrored catalog reads the files with the identity of its connection, so those permissions do not carry over by themselves. In Fabric, OneLake security roles on the mirrored item decide who sees what.

And you do not have to rebuild those roles by hand. Policy Weaver is an open-source Microsoft accelerator that copies Unity Catalog permissions into OneLake security roles. Direct Lake reports then show each user only what the roles allow. Before you rely on it:

  • Row filters and column masks: Policy Weaver translates simple group-based ones, but Fabric cannot mirror Unity Catalog tables that carry them yet (mirroring limitations). Check this first for your protected tables.
  • Identities: only account-level Databricks groups and users from Entra ID map, no workspace-local groups.
  • Not live: each run copies the current state, so a revoked permission counts only after the next run. Schedule it.
  • Workspace roles win: Admin, Member and Contributor see everything. Give report users the Viewer role or share the items.
  • Consumer setup: the SQL analytics endpoint must run in “User’s identity” mode, otherwise the roles are ignored.
  • Support: it is an MIT-licensed accelerator, provided as-is, not a product feature.

I did not test security in this benchmark. Test one protected table end to end, opened as a Viewer, before you promise it.

Four things that make Direct Lake on Databricks fast

  1. Write the gold tables in several files. Direct Lake loads columns in parallel per file and row group. A Databricks OPTIMIZE can compact a large table into a single file; check the file count afterwards. In my test, 16 files instead of one cut the warm page time by about 40 percent at 100 million rows.
  2. Keep column cardinality realistic. A measurement stored with four decimals has far more distinct values than one stored in watt hours, and distinct values cost memory.
  3. Warm the model after each refresh. One query page, run by the pipeline right after the reload.
  4. Keep Fabric and Databricks in the same region. For DirectQuery, distance cost 9 percent. For the Direct Lake cold load it cost more: up to 32 to 46 percent in my runs (region and shortcut Lakehouse changed together, so that is an upper bound).

Where the DirectQuery time goes

The Databricks query history explains the gap. Of an 11.7 second DirectQuery page (five queries one after another), the warehouse itself spent about 3 seconds, and many statements were even answered from Databricks’ result cache. The rest is the connector, Power BI’s formula engine and transit, on every click, for every visual. Direct Lake skips all of that once the columns are in memory.

My conclusion: never use DirectQuery for interactive reports on Databricks

Direct Lake was faster in every warm test, 1.4 to 3 times per page for a single user, and with ten people on the report at once it stayed at 1.4 to 4.2 seconds while DirectQuery climbed to 7 to 11. With a warm-up after each refresh, users do not see a cold start either. DirectQuery cannot catch up, because it pays the round trip to the warehouse for every visual on every click.

So for interactive Power BI reports on Databricks data, my rule is simple: never DirectQuery. Mirror the catalog and use Direct Lake, sized so the largest table fits in memory. If it does not fit, aggregations, a slimmer gold table or a larger capacity are the next step, not DirectQuery. Security does not change that either: Policy Weaver copies the Unity Catalog permissions to OneLake.

Databricks stays at the center of this picture. It holds the data, runs the pipelines and writes the Delta tables. Power BI just stops asking it the same question forty times a minute.

Notes on the test

The synthetic data is labelled as such. The REST numbers come from one evening (2026-10-27), one client location and mostly three to twelve repeats per measurement, and include about 0.7 seconds of API round trip on both sides. The browser clips are single recordings: the real-data clips on an F16 (2026-10-27), the 10-million-row, four-user and first-user clips on an F64 (2026-11-02), all with a Small serverless SQL warehouse. Browser times include the Fabric portal. The four “users” are four browser windows of the same account. Not tested: Import mode, aggregations, a DAX measure set tuned for DirectQuery, and V-Order. Raw results and scripts are in the project repository.

Leave a Reply