Datanest Digital | datanest.dev
This guide provides a structured decision framework for determining which Azure analytics
service to use for each workload in your data platform. Most mature Azure data platforms
benefit from running both Synapse and Databricks. The question is not "which one" but
"which workload goes where."
| Capability | Synapse Wins | Databricks Wins |
|---|---|---|
| T-SQL analytics | Yes | No |
| Serverless ad-hoc queries | Yes | No |
| Large-scale Spark ETL | No | Yes |
| ML model training | No | Yes |
| Delta Lake management | No | Yes |
| Power BI DirectQuery (DWH) | Yes | Comparable |
| Real-time streaming | No | Yes |
| Dedicated SQL warehousing | Yes | Comparable |
| Cost at low scale | Yes | No |
| Cost at high scale | No | Yes |
| Governance (Unity Catalog) | No | Yes |
| T-SQL stored procedures | Yes | No |
Use this tree top-down. Start with your workload type and follow the branches.
START HERE
│
├── T-SQL analytics / reporting ──────────> SYNAPSE (dedicated or serverless pool)
│
├── Ad-hoc data exploration ──────────────> See Level 2A
│
├── ETL / Data transformation ────────────> See Level 2B
│
├── Machine Learning ─────────────────────> DATABRICKS
│
├── Real-time streaming ──────────────────> DATABRICKS (Structured Streaming)
│
├── Data warehousing (star schema) ───────> See Level 2C
│
└── Data sharing / governance ────────────> See Level 2D
Ad-Hoc Data Exploration
│
├── Data is in Parquet/CSV/JSON on ADLS?
│ └── YES ──> SYNAPSE SERVERLESS
│ - OPENROWSET queries, no cluster spin-up
│ - Pay per TB scanned (~$5/TB)
│ - Ideal for infrequent, schema-discovery queries
│
├── Data is in Delta Lake?
│ ├── Need T-SQL interface?
│ │ └── YES ──> SYNAPSE SERVERLESS (with Delta support)
│ └── Need full Spark/Python interface?
│ └── YES ──> DATABRICKS SQL or notebooks
│
└── Data volume per query > 1 TB regularly?
└── YES ──> DATABRICKS SQL WAREHOUSE
- More cost-effective at high scan volumes
- Better query optimization for Delta
ETL / Data Transformation
│
├── Transformation language?
│ ├── T-SQL only (CTAS, views, stored procs)
│ │ └── SYNAPSE DEDICATED POOL or SERVERLESS
│ │
│ ├── PySpark / Scala Spark
│ │ ├── Workload < 2 hours/day, < 500 GB?
│ │ │ └── EITHER (cost-compare with calculator tool)
│ │ └── Workload > 2 hours/day or > 500 GB?
│ │ └── DATABRICKS (Photon engine, better autoscaling)
│ │
│ └── Mixed T-SQL + Spark
│ └── BOTH: Databricks for Spark, Synapse for T-SQL layers
│
├── Need Delta Lake MERGE / UPSERT?
│ └── YES ──> DATABRICKS
│ - Native Delta engine with optimized MERGE
│ - Synapse Spark has Delta support but less optimized
│
├── Need orchestration?
│ ├── Simple pipelines ──> Synapse Pipelines (built-in ADF)
│ ├── Complex DAGs ──> Databricks Workflows or external orchestrator
│ └── Enterprise orchestration ──> Azure Data Factory (works with both)
│
└── Need incremental processing?
├── Change Data Feed (Delta) ──> DATABRICKS
└── Watermark-based ──> EITHER
Data Warehousing
│
├── Existing T-SQL codebase to maintain?
│ └── YES ──> SYNAPSE DEDICATED POOL
│ - MPP engine compatible with SQL Server patterns
│ - Distribution, indexing, materialized views
│
├── Building new from scratch?
│ ├── Team has strong SQL Server / T-SQL skills?
│ │ └── YES ──> SYNAPSE DEDICATED POOL
│ ├── Team prefers Spark + SQL hybrid?
│ │ └── YES ──> DATABRICKS SQL WAREHOUSE
│ └── Need lakehouse architecture (no data copies)?
│ └── YES ──> DATABRICKS SQL WAREHOUSE over Delta Lake
│
├── Concurrency requirements?
│ ├── < 30 concurrent queries ──> EITHER
│ └── > 30 concurrent queries ──> DATABRICKS SQL WAREHOUSE
│ - Better concurrency scaling with serverless SQL warehouses
│
└── Data freshness requirements?
├── Near real-time (< 5 min) ──> DATABRICKS (streaming into Delta)
└── Batch (hourly+) ──> EITHER
Data Sharing & Governance
│
├── Cross-workspace data sharing?
│ └── DATABRICKS UNITY CATALOG
│ - Centralized metastore across workspaces
│ - Row/column-level security
│ - Data lineage tracking
│
├── Need to share data with T-SQL consumers?
│ └── BOTH: Unity Catalog for governance + Synapse serverless for T-SQL access
│
├── External data sharing (outside organization)?
│ ├── Delta Sharing protocol ──> DATABRICKS
│ └── Via SQL endpoint ──> SYNAPSE or DATABRICKS SQL
│
└── Audit and compliance?
├── Unity Catalog audit logs ──> DATABRICKS
└── Synapse + Purview integration ──> SYNAPSE
Best for: Organizations with strong T-SQL reporting teams and large-scale Spark ETL.
Ingestion ──> Databricks (Bronze/Silver/Gold) ──> ADLS Gen2 (Delta) ──> Synapse Serverless ──> Power BI
When to use:
When to avoid:
Best for: Organizations with an existing Synapse dedicated pool running star-schema workloads.
Sources ──> ADF/Synapse Pipelines ──> Synapse Dedicated Pool (DWH)
──> ADLS Gen2 ──> Databricks (ML training)
When to use:
When to avoid:
Best for: New builds adopting the lakehouse architecture.
Ingestion ──> Databricks (medallion architecture) ──> Delta Lake on ADLS Gen2
├──> Databricks SQL (primary)
└──> Synapse Serverless (T-SQL access)
When to use:
When to avoid:
Best for: Organizations migrating from Synapse Spark pools to Databricks.
Phase 1: Synapse Spark + Synapse SQL (current)
Phase 2: Databricks Spark + Synapse SQL (transition -- use migration guide)
Phase 3: Databricks Spark + Databricks SQL + Synapse Serverless (target)
Use the included tools/cost_comparison_calculator.py for detailed estimates. Here are
general guidelines:
| Scenario | Reason |
|---|---|
| < 10 TB scanned/month (serverless) | Pay-per-query at $5/TB is hard to beat |
| DWH workload < DW1000c | Fixed-cost dedicated pool |
| Sporadic query patterns | Serverless has zero idle cost |
| Existing EA/CSP discounts on Synapse | Negotiated pricing |
| Scenario | Reason |
|---|---|
| > 4 hours/day Spark compute | Photon engine throughput advantage |
| High-concurrency SQL workload | Serverless SQL warehouse autoscaling |
| ML training workloads | GPU cluster support, MLflow included |
| Data volumes > 10 TB per pipeline run | Better Spark optimization |
| Scenario | Approach |
|---|---|
| Heavy Spark ETL + light SQL reporting | Databricks for ETL, Synapse serverless for reporting |
| Mixed T-SQL DWH + ML | Synapse dedicated for DWH, Databricks for ML |
| Large Delta Lake + diverse consumers | Databricks manages Delta, Synapse provides T-SQL access |
Use this worksheet to map your current workloads. Fill in each row and use the
decision tree above to determine the target service.
| # | Workload Name | Type | Current Service | Data Volume | Frequency | Target Service | Rationale |
|---|---|---|---|---|---|---|---|
| 1 | |||||||
| 2 | |||||||
| 3 | |||||||
| 4 | |||||||
| 5 | |||||||
| 6 | |||||||
| 7 | |||||||
| 8 |
Synapse Spark pools lack Photon, have slower autoscaling, and limited cluster
configuration. If your Spark workloads exceed 2 hours/day, Databricks is typically
30-50% more cost-effective due to Photon and better cluster management.
If your reporting team writes T-SQL and data volumes are small (< 10 TB/month scanned),
Synapse serverless SQL is simpler and cheaper. There is no need to spin up a Databricks
SQL warehouse for 20 queries per day.
The worst outcome is copying data into both Synapse dedicated pools AND Delta Lake.
Choose ONE storage layer (Delta Lake on ADLS Gen2 is recommended) and have both services
read from it. Synapse serverless can query Delta directly.
If your dedicated pool runs at DW2000c+ but utilization is low outside business hours,
either implement pause/resume automation or consider moving to Databricks SQL serverless
which scales to zero.
If you run Databricks, enable Unity Catalog from day one. Retrofitting governance is
expensive. Synapse consumers can still access Unity Catalog-managed tables via Synapse
serverless views over Delta Lake.
If you are currently on Synapse Spark pools and considering Databricks, see the
companion guide: guides/migration_synapse_to_databricks.md.
Key migration triggers:
Before finalizing your architecture, verify:
guides/security_cross_service.md)guides/security_cross_service.md -- Authentication patternsguides/migration_synapse_to_databricks.md -- Migration playbooksql/serverless_views_over_delta.sql -- Synapse-to-Delta integration SQLtools/cost_comparison_calculator.py -- Automated cost comparison*Datanest Digital -- datanest.dev*
Datanest Digital | datanest.dev
This guide covers the end-to-end migration from Azure Synapse Spark pools to Azure
Databricks. The migration preserves your existing data in ADLS Gen2 (no data movement
required) and focuses on compute, code, and orchestration changes.
Phase 1: Assessment & Planning (1-2 weeks)
Phase 2: Infrastructure Provisioning (1 week)
Phase 3: Code Migration (2-4 weeks)
Phase 4: Orchestration Migration (1-2 weeks)
Phase 5: Validation & Cutover (1-2 weeks)
Catalog every Spark asset in your Synapse workspace:
| Asset Type | Count | Migration Complexity |
|---|---|---|
| Spark notebooks | Low-Medium | |
| Spark job definitions | Medium | |
| Spark pools (configurations) | Low | |
| Linked services (Spark-related) | Medium | |
| Synapse pipeline Spark activities | Medium | |
| Spark SQL databases/tables | Low | |
| Custom libraries (.whl, .jar) | Low | |
| Spark configurations | Low |
Search your notebooks for Synapse-specific APIs that need translation:
| Synapse API | Databricks Equivalent |
|---|---|
mssparkutils.fs.* | dbutils.fs.* |
mssparkutils.notebook.run() | dbutils.notebook.run() |
mssparkutils.credentials.getSecret() | dbutils.secrets.get() |
mssparkutils.env.getWorkspaceName() | spark.conf.get("spark.databricks.workspaceUrl") |
spark.read.synapsesql() | JDBC connection or lakehouse tables |
df.write.synapsesql() | JDBC connection or lakehouse tables |
TokenLibrary.getConnectionString() | dbutils.secrets.get() or Unity Catalog connections |
| Synapse linked service references | Databricks secrets or Unity Catalog |
Map each Synapse Spark pool to a Databricks cluster configuration:
| Synapse Setting | Databricks Equivalent |
|---|---|
| Node size (Small/Medium/Large) | Worker instance type |
| Autoscale (min/max nodes) | Autoscale (min/max workers) |
| Spark version | Databricks Runtime version |
| Python version | Included in DBR |
| Required libraries | Cluster libraries or init scripts |
| Spark config properties | Cluster Spark config |
| Dynamic executor allocation | Databricks autoscaling |
Node size mapping (approximate):
| Synapse Node Size | vCores | Memory | Databricks VM (Azure) |
|---|---|---|---|
| Small | 4 | 32 GB | Standard_DS4_v2 |
| Medium | 8 | 64 GB | Standard_DS5_v2 |
| Large | 16 | 128 GB | Standard_E16s_v3 |
| XLarge | 32 | 256 GB | Standard_E32s_v3 |
| XXLarge | 64 | 512 GB | Standard_E64s_v3 |
Use the included Terraform module at terraform/synapse-databricks/main.tf to deploy:
The most critical step. Your ADLS Gen2 accounts must be accessible from Databricks.
Option A: Unity Catalog External Locations (recommended)
-- In Databricks SQL
CREATE STORAGE CREDENTIAL adls_credential
WITH (AZURE_MANAGED_IDENTITY = '<managed-identity-id>');
CREATE EXTERNAL LOCATION bronze_lake
URL 'abfss://bronze@<storage>.dfs.core.windows.net/'
WITH (STORAGE CREDENTIAL adls_credential);Option B: Cluster-Level ADLS Access
# Spark config on cluster or in notebook
spark.conf.set(
"fs.azure.account.key.<storage>.dfs.core.windows.net",
dbutils.secrets.get(scope="storage", key="account-key")
)If you used Synapse Spark SQL databases and tables, recreate them in Databricks:
-- Create Unity Catalog schema matching Synapse Spark database
CREATE SCHEMA IF NOT EXISTS catalog_name.schema_name;
-- Register existing Delta tables (no data copy needed)
CREATE TABLE catalog_name.schema_name.table_name
USING DELTA
LOCATION 'abfss://container@storage.dfs.core.windows.net/path/to/table';Upload custom .whl and .jar files to:
CREATE VOLUME catalog.schema.libs/FileStore/jars/ (legacy approach)/Workspace/Shared/libs/Step 1: Export from Synapse
Export notebooks from Synapse Studio as .ipynb or .py files. Use the Synapse REST API
for bulk export:
# List all notebooks
az synapse notebook list \
--workspace-name <workspace> \
--query "[].name" -o tsv
# Export each notebook
az synapse notebook export \
--workspace-name <workspace> \
--name <notebook_name> \
--output-folder ./exported_notebooks/Step 2: API Translation
Apply these find-and-replace patterns across all exported notebooks:
# mssparkutils.fs -> dbutils.fs
# Direct replacement -- API is nearly identical
# mssparkutils.fs.ls() -> dbutils.fs.ls()
# mssparkutils.fs.cp() -> dbutils.fs.cp()
# mssparkutils.fs.mv() -> dbutils.fs.mv()
# mssparkutils.fs.rm() -> dbutils.fs.rm()
# mssparkutils.fs.mkdirs() -> dbutils.fs.mkdirs()
# mssparkutils.fs.head() -> dbutils.fs.head()
# Secrets
# mssparkutils.credentials.getSecret("kv-name", "secret-name")
# -> dbutils.secrets.get(scope="kv-name", key="secret-name")
# Notebook orchestration
# mssparkutils.notebook.run("path", timeout, {"param": "val"})
# -> dbutils.notebook.run("path", timeout, {"param": "val"})
# Exit values
# mssparkutils.notebook.exit("value")
# -> dbutils.notebook.exit("value")Step 3: Handle Synapse SQL Connector
Replace synapsesql connector calls with JDBC or lakehouse patterns:
# BEFORE (Synapse)
df = spark.read.synapsesql("pool.schema.table")
df.write.synapsesql("pool.schema.table", mode="overwrite")
# AFTER (Databricks) -- Option 1: JDBC
jdbc_url = "jdbc:sqlserver://<synapse-endpoint>:1433;database=<db>"
df = spark.read.jdbc(url=jdbc_url, table="schema.table",
properties={"accessToken": token})
# AFTER (Databricks) -- Option 2: Unity Catalog lakehouse table
df = spark.read.table("catalog.schema.table")
df.write.mode("overwrite").saveAsTable("catalog.schema.table")Step 4: Import to Databricks
# Use Databricks CLI for bulk import
databricks workspace import-dir \
./exported_notebooks/ \
/Workspace/Shared/migrated/ \
--overwriteConvert Synapse Spark job definitions to Databricks job definitions:
// Databricks Job JSON equivalent
{
"name": "etl_daily_pipeline",
"tasks": [
{
"task_key": "ingest",
"notebook_task": {
"notebook_path": "/Workspace/Shared/migrated/ingest",
"base_parameters": {
"date": "{{job.trigger_time.iso_date}}"
}
},
"job_cluster_key": "etl_cluster"
}
],
"job_clusters": [
{
"job_cluster_key": "etl_cluster",
"new_cluster": {
"spark_version": "14.3.x-scala2.12",
"node_type_id": "Standard_DS4_v2",
"autoscale": {
"min_workers": 2,
"max_workers": 8
}
}
}
]
}Replace Synapse pipeline Spark activities with Databricks activities:
| Synapse Pipeline Activity | Replacement |
|---|---|
| Synapse Notebook activity | Databricks Notebook activity (in ADF/Synapse Pipeline) |
| Spark job definition activity | Databricks JAR / Python activity |
| Spark SQL activity | Databricks notebook with %sql cells |
If using Azure Data Factory, add a Databricks linked service and replace activity
references. The pipeline structure can remain identical.
If you want to consolidate orchestration into Databricks:
1. Map each pipeline to a Databricks multi-task job
2. Convert pipeline parameters to job parameters
3. Map pipeline triggers to Databricks job triggers
4. Replace ADF data flow activities with Spark notebooks
Run both Synapse Spark and Databricks pipelines in parallel for 1-2 weeks:
# Validation notebook -- compare outputs
synapse_output = spark.read.delta("abfss://...synapse_output_path...")
databricks_output = spark.read.delta("abfss://...databricks_output_path...")
# Row count comparison
assert synapse_output.count() == databricks_output.count(), \
f"Row count mismatch: {synapse_output.count()} vs {databricks_output.count()}"
# Schema comparison
assert synapse_output.schema == databricks_output.schema, \
"Schema mismatch detected"
# Data comparison (sample-based for large datasets)
diff = synapse_output.exceptAll(databricks_output)
assert diff.count() == 0, f"Data differences found: {diff.count()} rows"Compare execution metrics:
| Metric | Synapse Spark | Databricks | Delta |
|---|---|---|---|
| Job duration | |||
| Data processed (GB) | |||
| Cluster spin-up time | |||
| Cost per run |
After 2-4 weeks of stable Databricks operation:
1. Delete Synapse Spark pool configurations
2. Remove Spark-related linked services
3. Archive exported Synapse notebooks
4. Update documentation and runbooks
5. Retain Synapse workspace if still using SQL pools or pipelines
| Issue | Resolution |
|---|---|
mssparkutils not found | Replace with dbutils equivalents |
| Linked service auth failures | Use Databricks secret scopes or Unity Catalog connections |
| Different Spark version behavior | Pin Databricks Runtime to compatible version |
| Missing Python packages | Add to cluster libraries or requirements.txt |
| ADLS Gen2 permission denied | Configure storage credential in Unity Catalog |
| Different default file formats | Explicitly specify format in read/write calls |
| Timezone differences | Set spark.sql.session.timeZone explicitly |
Once migrated, take advantage of Databricks features not available in Synapse Spark:
1. Enable Photon -- 2-8x speedup on Delta Lake queries
2. Use Unity Catalog -- Centralized governance
3. Delta Live Tables -- Declarative ETL pipelines
4. Structured Streaming -- Replace batch with streaming where applicable
5. MLflow -- Integrated ML experiment tracking
6. Databricks Asset Bundles -- CI/CD for notebooks and jobs
*Datanest Digital -- datanest.dev*
Get the full Azure Synapse-Databricks Integration Kit and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.