Ingest data from self-hosted MySQL
View as MarkdownThis page shows you how to stream data from a self-hosted MySQL database to Materialize using the MySQL source.
Before you begin
-
Make sure you are running MySQL 8.0.1+ with support for GTID-based binary log (binlog) replication.
-
Ensure you have access to your MySQL instance via the
mysqlclient, or your preferred SQL client.
A. Configure MySQL
1. Enable GTID-based binlog replication
Before creating a source in Materialize, you must configure your MySQL database for GTID-based binlog replication. Ensure the upstream MySQL database has been configured for GTID-based binlog replication:
| MySQL Configuration | Value | Notes |
|---|---|---|
log_bin
|
ON
|
|
binlog_row_image
|
FULL
|
|
binlog_row_metadata
|
FULL
|
|
binlog_format
|
ROW
|
Deprecated as of MySQL 8.0.34. Newer versions of MySQL default to row-based logging. |
gtid_mode
|
ON
|
|
enforce_gtid_consistency
|
ON
|
|
replica_preserve_commit_order
|
ON
|
Only required when connecting Materialize to a read-replica. |
For guidance on enabling GTID-based binlog replication, see the MySQL documentation.
2. Create a user for replication
Once GTID-based binlog replication is enabled, we recommend creating a dedicated user for Materialize with sufficient privileges to manage replication.
-
As a superuser, use
mysql(or your preferred SQL client) to connect to your database. -
Create a dedicated user for Materialize, if you don’t already have one:
CREATE USER 'materialize'@'%' IDENTIFIED BY '<password>'; ALTER USER 'materialize'@'%' REQUIRE SSL;IAM authentication with AWS RDS for MySQL is also supported. See the Amazon RDS User Guide for instructions on enabling IAM database authentication, creating IAM policies, and creating a database account.
-
Grant the user permission to manage replication:
GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO 'materialize'@'%';Once connected to your database, Materialize will take an initial snapshot of the tables in your MySQL server.
SELECTprivileges are required for this initial snapshot. -
Apply the changes:
FLUSH PRIVILEGES;
B. (Optional) Configure network security
There are various ways to configure your database’s network to allow Materialize to connect:
-
Allow Materialize IPs: If your database is publicly accessible, you can configure your database’s firewall to allow connections from a set of static Materialize IP addresses.
-
Use an SSH tunnel: If your database is running in a private network, you can use an SSH tunnel to connect Materialize to the database.
Select the option that works best for you.
-
In the SQL Shell, or your preferred SQL client connected to Materialize, find the static egress IP addresses for the Materialize region you are running in:
SELECT * FROM mz_egress_ips; -
Update your database firewall rules to allow traffic from each IP address from the previous step.
To create an SSH tunnel from Materialize to your database, you launch an VM to serve as an SSH bastion host, configure the bastion host to allow traffic only from Materialize, and then configure your database’s private network to allow traffic from the bastion host.
-
Launch a VM to serve as your SSH bastion host.
- Make sure the VM is publicly accessible and in the same VPC as your database.
- Add a key pair and note the username. You’ll use this username when connecting Materialize to your bastion host.
- Make sure the VM has a static public IP address. You’ll use this IP address when connecting Materialize to your bastion host.
-
Configure the SSH bastion host to allow traffic only from Materialize.
-
In the SQL Shell, or your preferred SQL client connected to Materialize, get the static egress IP addresses for the Materialize region you are running in:
SELECT * FROM mz_egress_ips; -
Update your SSH bastion host’s firewall rules to allow traffic from each IP address from the previous step.
-
-
Update your database firewall rules to allow traffic from the SSH bastion host.
Configure your network to allow Materialize to connect to your database. For example, you can:
-
Allow Materialize IPs: Configure your database’s security group to allow connections from Materialize.
-
Use an SSH tunnel: Use an SSH tunnel to connect Materialize to the database.
- Update your database firewall rules to allow traffic from Materialize IPs.
To create an SSH tunnel from Materialize to your database, you launch an VM to serve as an SSH bastion host, configure the bastion host to allow traffic only from Materialize, and then configure your database’s private network to allow traffic from the bastion host.
-
Launch a VM to serve as your SSH bastion host.
- Make sure the VM is publicly accessible and in the same VPC as your database.
- Add a key pair and note the username. You’ll use this username when connecting Materialize to your bastion host.
- Make sure the VM has a static public IP address. You’ll use this IP address when connecting Materialize to your bastion host.
-
Configure the SSH bastion host to allow traffic only from Materialize.
-
Update your database firewall rules to allow traffic from the SSH bastion host.
C. Ingest data in Materialize
1. (Optional) Create a cluster
quickstart), you can skip this step. For production
scenarios, we recommend separating your workloads into multiple clusters for
resource isolation.
In Materialize, a cluster is an isolated environment, similar to a virtual warehouse in Snowflake. When you create a cluster, you choose the size of its compute resource allocation based on the work you need the cluster to do, whether ingesting data from a source, computing always-up-to-date query results, serving results to clients, or a combination.
In this case, you’ll create a dedicated cluster for ingesting source data from your MySQL database.
-
In the SQL Shell, or your preferred SQL client connected to Materialize, use the
CREATE CLUSTERcommand to create the new cluster:CREATE CLUSTER ingest_mysql (SIZE = '200cc'); SET CLUSTER = ingest_mysql;A cluster of size
200ccshould be enough to process the initial snapshot of the tables in your MySQL database. For very large snapshots, consider using a larger size to speed up processing. Once the snapshot is finished, you can readjust the size of the cluster to fit the volume of changes being replicated from your upstream MySQL database.
2. Create a connection
Once you have configured your network, create a connection in Materialize per your networking configuration.
-
In the SQL Shell, or your preferred SQL client connected to Materialize, use the
CREATE SECRETcommand to securely store the password for thematerializeMySQL user you created earlier:CREATE SECRET mysqlpass AS '<PASSWORD>';For AWS IAM authentication, you must create a connection to AWS. See the
CREATE CONNECTIONcommand for details. -
Use the
CREATE CONNECTIONcommand to create a connection object with access and authentication details for Materialize to use:CREATE CONNECTION mysql_connection TO MYSQL ( HOST <host>, PORT 3306, USER 'materialize', PASSWORD SECRET mysqlpass, SSL MODE REQUIRED );- Replace
<host>with your MySQL endpoint.
AWS IAM authentication is also available, see the
CREATE CONNECTIONcommand for details. - Replace
-
In the SQL Shell, or your preferred SQL client connected to Materialize, use the
CREATE CONNECTIONcommand to create an SSH tunnel connection:CREATE CONNECTION ssh_connection TO SSH TUNNEL ( HOST '<SSH_BASTION_HOST>', PORT <SSH_BASTION_PORT>, USER '<SSH_BASTION_USER>' );-
Replace
<SSH_BASTION_HOST>and<SSH_BASTION_PORT> with the public IP address and port of the SSH bastion host you created earlier. -
Replace
<SSH_BASTION_USER>with the username for the key pair you created for your SSH bastion host.
-
-
Get Materialize’s public keys for the SSH tunnel connection:
SELECT * FROM mz_ssh_tunnel_connections; -
Log in to your SSH bastion host and add Materialize’s public keys to the
authorized_keysfile, for example:# Command for Linux echo "ssh-ed25519 AAAA...76RH materialize" >> ~/.ssh/authorized_keys echo "ssh-ed25519 AAAA...hLYV materialize" >> ~/.ssh/authorized_keys -
Back in the SQL client connected to Materialize, validate the SSH tunnel connection you created using the
VALIDATE CONNECTIONcommand:VALIDATE CONNECTION ssh_connection;If no validation error is returned, move to the next step.
-
Use the
CREATE SECRETcommand to securely store the password for thematerializeMySQL user you created earlier:CREATE SECRET mysqlpass AS '<PASSWORD>';For AWS IAM authentication, you must create a connection to AWS. See the
CREATE CONNECTIONcommand for details. -
Use the
CREATE CONNECTIONcommand to create another connection object, this time with database access and authentication details for Materialize to use:CREATE CONNECTION mysql_connection TO MYSQL ( HOST '<host>', SSH TUNNEL ssh_connection );- Replace
<host>with your MySQL endpoint.
- Replace
AWS IAM authentication is also available, see the CREATE CONNECTION
command for details.
3. Start ingesting data
Once you have created the connection, you can:
- use the connection in the
CREATE SOURCEcommand to connect to your MySQL instance, and - use the source in
CREATE TABLE FROM SOURCEto create tables in Materialize and start ingesting data:
-- Step 1: Create the source
CREATE SOURCE mz_source
FROM MYSQL CONNECTION mysql_connection;
-- Step 2: Create tables from the source
CREATE TABLE t FROM SOURCE mz_source (REFERENCE public.t);
- By default, the source will be created in the active cluster; to use a different cluster, use the
IN CLUSTERclause. - After creating the source, you can query
mz_internal.mz_source_referencesto see all available tables. - Create individual tables using
CREATE TABLE <table_name> FROM SOURCE <source_name> (REFERENCE <upstream_table>). - To handle unsupported data types, use
WITH (TEXT COLUMNS = [<column>])orWITH (EXCLUDE COLUMNS = [<column>])in theCREATE TABLEstatement.
After source creation, refer to schema changes considerations for information on handling upstream schema changes.
Once you have created the connection, you can use the connection in the
CREATE SOURCE command to connect to your MySQL instance and start ingesting
data:
CREATE SOURCE mz_source
FROM MYSQL CONNECTION mysql_connection
FOR ALL TABLES;
-
By default, the source will be created in the active cluster; to use a different cluster, use the
IN CLUSTERclause. -
To ingest data from specific schemas or tables, use the
FOR SCHEMAS (<schema1>,<schema2>)orFOR TABLES (<table1>, <table2>)options instead ofFOR ALL TABLES. -
To handle unsupported data types, use the
TEXT COLUMNSorEXCLUDE COLUMNSoptions.
After source creation, refer to schema changes considerations for information on handling upstream schema changes.
4. Monitor the ingestion status
Before it starts consuming the replication stream, Materialize takes a snapshot of the relevant tables. Until this snapshot is complete, Materialize won’t have the same view of your data as your MySQL database.
In this step, you’ll first verify that the source is running and then check the status of the snapshotting process.
-
Back in the SQL client connected to Materialize, use the
mz_source_statusestable to check the overall status of your source:WITH source_ids AS (SELECT id FROM mz_sources WHERE name = 'mz_source') SELECT * FROM mz_internal.mz_source_statuses JOIN ( SELECT referenced_object_id FROM mz_internal.mz_object_dependencies WHERE object_id IN (SELECT id FROM source_ids) UNION SELECT id FROM source_ids ) AS sources ON mz_source_statuses.id = sources.referenced_object_id;For each
subsource, make sure thestatusisrunning. If you seestalledorfailed, there’s likely a configuration issue for you to fix. Check theerrorfield for details and fix the issue before moving on. Also, if thestatusof any subsource isstartingfor more than a few minutes, contact our team. -
Once the source is running, use the
mz_source_statisticstable to check the status of the initial snapshot:WITH source_ids AS (SELECT id FROM mz_sources WHERE name = 'mz_source') SELECT sources.referenced_object_id AS id, mz_sources.name, snapshot_committed FROM mz_internal.mz_source_statistics JOIN ( SELECT object_id, referenced_object_id FROM mz_internal.mz_object_dependencies WHERE object_id IN (SELECT id FROM source_ids) UNION SELECT id, id FROM source_ids ) AS sources ON mz_source_statistics.id = sources.referenced_object_id JOIN mz_sources ON mz_sources.id = sources.referenced_object_id;object_id | snapshot_committed ----------|------------------ u144 | t (1 row)Once
snapshot_commitedist, move on to the next step. Snapshotting can take between a few minutes to several hours, depending on the size of your dataset and the size of the cluster the source is running in.
5. Right-size the cluster
After the snapshotting phase, Materialize starts ingesting change events from
the MySQL replication stream. For this work, Materialize generally
performs well with a 100cc replica, so you can resize the cluster
accordingly.
-
Still in a SQL client connected to Materialize, use the
ALTER CLUSTERcommand to downsize the cluster to100cc:ALTER CLUSTER ingest_mysql SET (SIZE '100cc');Behind the scenes, this command adds a new
100ccreplica and removes the200ccreplica. -
Use the
SHOW CLUSTER REPLICAScommand to check the status of the new replica:SHOW CLUSTER REPLICAS WHERE cluster = 'ingest_mysql';cluster | replica | size | ready -----------------+---------+--------+------- ingest_mysql | r1 | 100cc | t (1 row)
D. Explore your data
With Materialize ingesting your MySQL data into durable storage, you can start exploring the data, computing real-time results that stay up-to-date as new data arrives, and serving results efficiently.
-
Explore your data with
SHOW SOURCESandSELECT. -
Compute real-time results in memory with
CREATE VIEWandCREATE INDEXor in durable storage withCREATE MATERIALIZED VIEW. -
Serve results to a PostgreSQL-compatible SQL client or driver with
SELECTorSUBSCRIBEor to an external message broker withCREATE SINK. -
Check out the tools and integrations supported by Materialize.
Considerations
Supported types
Materialize natively supports the following MySQL types:
bigintbinarybitblobbooleanchardatedatetimedecimaldoublefloatintjsonlongbloblongtextmediumblobmediumintmediumtextnumericrealsmallinttexttimetimestamptinyblobtinyinttinytextvarbinaryvarchar
When replicating tables that contain the unsupported data types, you can:
-
Use
TEXT COLUMNSoption for the following unsupported MySQL types:enumyear
The specified columns will be treated as
textand will not offer the expected MySQL type features. -
Use the
EXCLUDE COLUMNSoption to exclude any columns that contain unsupported data types.
Zero values for date, datetime, and timestamp
MySQL allows the special “zero” values 0000-00-00, 0000-00-00 00:00:00 in date, datetime, and timestamp columns when the server
sql_mode does not include NO_ZERO_DATE or NO_ZERO_IN_DATE. These
values are not representable in Materialize’s corresponding native types,
so they will cause ingestion to fail for the affected column.
To ingest columns that contain zero values, use TEXT COLUMNS to
decode the affected columns as text. The zero values for date,
datetime, timestamp, and year are preserved verbatim as strings
(e.g. "0000-00-00 00:00:00", "0000").
Modifying an existing source
When you add a new subsource to an existing source (ALTER SOURCE ... ADD SUBSOURCE ...), Materialize starts the snapshotting
process for the new subsource. During this snapshotting, the data ingestion for
the existing subsources for the same source is temporarily blocked. As such, if
possible, you can resize the cluster to speed up the snapshotting process and
once the process finishes, resize the cluster for steady-state.
Handling upstream operations
This section describes how changes to upstream tables that Materialize ingests affect the corresponding Materialize tables.
Adding a column
When you add a new column to your upstream table, Materialize continues to ingest only the existing columns.
To incorporate the new column:
-
If using the new
CREATE SOURCEandCREATE TABLE FROM SOURCEsyntax, create a new table from the source. See Handle upstream column addition. -
If using the legacy
CREATE SOURCE ... FOR ...syntax that creates subsources, useDROP SOURCEto drop the affected subsource, and then add the table back to the source usingALTER SOURCE ... ADD SUBSOURCE. The re-added subsource includes the new column.
Dropping a column
Dropping columns that Materialize does not ingest (for example, columns added after the source was created, or columns that are excluded) is supported. As these columns were never ingested, you can drop them without issue.
If your Materialize source ingests a column, dropping that column from your upstream table puts the affected table into an error state.
-
If using the new
CREATE SOURCEandCREATE TABLE FROM SOURCEsyntax, you can safely drop a column by first ignoring it in Materialize. See Handle upstream column drop. -
If using legacy
CREATE SOURCE ... FOR ...syntax, useDROP SOURCEto drop the affected subsource, and then add the table back to the source usingALTER SOURCE ... ADD SUBSOURCE.
Changing constraints
Materialize ignores the following constraint changes: foreign
key and CHECK.
As such, you can add or drop them without affecting ingestion.
Materialize also ignores NOT NULL, UNIQUE, and PRIMARY KEY constraints that
are added after the Materialize table is created (that is, the table was created
without them). Adding such a constraint, and later dropping it, does not affect
ingestion.
Dropping a NOT NULL, UNIQUE, or PRIMARY KEY constraint that existed when
the table was created puts the affected table into an error state.
Changing a column’s data type
Changing an ingested column’s data type upstream so that it maps to a different Materialize type than before puts the affected Materialize table into an error state. Ingestion for that table stops, and you must drop and recreate the table in Materialize to resume ingestion.
Changing an ingested column’s upstream data type so that it continues to map to
the same Materialize type does not interrupt ingestion. For example, changing
tinyint to smallint, changing within the
text/tinytext/mediumtext/longtext family, and adjusting bit(n)
precision are all safe.
Appending new values to the end of an existing enum does not put the table into an error state. However, the newly-added values are not recognized, so rows that use them fail to decode until you drop and recreate the table. Existing enum values remain recognized, and rows that use them continue to decode successfully.
Any other enum change puts the affected Materialize table into an error state, including inserting a value before the end, reordering or renaming values, and removing values.
Renaming a column
Renaming a column that Materialize ingests puts the affected table into an error state. Ingestion for that table stops, and you must drop and recreate the table in Materialize to resume ingestion.
Table-level operations
The following upstream operations put the affected table into an error state. Ingestion for that table stops, and you must drop and recreate the affected table in Materialize to resume:
- Dropping a table (
DROP TABLE). - Renaming a table or moving it to a different schema.
- Truncating a table (
TRUNCATE). To clear a table without putting it into an error state, use an unqualifiedDELETE FROM t;instead.