Maxwell Documentation

repository·master·Indexed 26 days ago

https://github.com/zendesk/maxwell

Maxwell is a change data capture (CDC) tool that monitors MySQL binlogs and streams row-level changes as JSON to platforms such as Kafka, Kinesis, SQS, SNS, Google Cloud Pub/Sub, BigQuery, RabbitMQ, and Redis. It supports bootstrapping existing datasets, daemon mode execution, and various producer configurations for ETL processes, audit logs, and search indexing.

Tokens
19K
Snippets
32
Records
109
Agent score
86%

What's inside Maxwell

  1. Overview of Maxwell's daemon

    master
    Maxwell's daemon is a change data capture (CDC) application designed to read MySQL binlogs and stream data changes as JSON to various platforms, including Kafka, Kinesis, and other streaming services. It is commonly used for ETL processes, maintaining database audit logs, cache management, search indexing, and inter-service communication.
  2. Understand Maxwell schema storage and history

    master

    Maxwell tracks database schema changes to interpret raw MySQL binlog bytes as typed data (numbers, strings, etc.). It uses a combination of base tables and a delta-based history system stored in the maxwell database.

    Base Schema Tables

    When Maxwell first runs, it captures the initial schema in these tables:

    • tables
    • columns
    • databases

    Schema Change History

    As schema modifications occur in the binlog, Maxwell stores the changes (diffs) in the schemas table. Each entry in schemas contains:

    • binlog_file, binlog_position (or gtid_set): The exact binlog location of the change.
    • deltas: The internal representation of the schema change.
    • base_schema_id: The ID of the previous schema this delta applies to.
    • last_heartbeat_read: The most recent Maxwell heartbeat seen in the binlog before this change.
    • server_id: The identifier for the database server.

    To reconstruct the schema at any specific binlog position, Maxwell finds the most recent schema for the server_id that occurred before that position, then follows the base_schema_id chain back to the initial captured schema.

  3. Implement a Custom Producer

    master

    If existing producers do not meet your needs, you can add a custom producer at runtime:

    1. Implement Interfaces: Implement the ProducerFactory interface (to create your AbstractProducer) and the AbstractProducer itself.
    2. Register Factory: Set custom_producer.factory in your configuration to the fully qualified class name of your ProducerFactory.
    3. Deploy JAR: Add the custom ProducerFactory JAR and all its dependencies to the $MAXWELL_HOME/lib directory.
    4. Configure: Use the custom_producer.* (or CUSTOM_PRODUCER_* env var) namespace for your producer's specific settings. These are accessible via MaxwellConfig.customProducerProperties.
  4. Implement custom logic with Javascript Filters

    master

    For complex filtering or data munging requirements, you can provide a Javascript file via the --javascript FILE flag.

    Your script must contain a function named process_row(row, state).

    Arguments:

    • row: A WrappedRowMap object representing the current row. It provides methods like row.suppress() to drop the row and row.data.get(key) / row.data.put(key, value) to access/modify row data.
    • state: A LinkedHashMap<String, Object> representing a global state that persists across rows. You can use state.put(key, value) and state.get(key) to maintain state for filtering decisions (e.g., tracking a flag across multiple rows).

    Capabilities:

    • Suppressing rows: Call row.suppress() to prevent the row from being emitted.
    • Data Munging: Modify the row data directly using row.data.put() before it is emitted.
    function process_row(row, state) {
    	// Example: Updating global state based on row values
    	if ( row.database == "test" && row.table == "lock") {
    		var haslock = row.data.get("haslock");
    		if ( haslock == "false" ) {
    			state.put("haslock", "false");
    		} else if( haslock == "true" ) {
    			state.put("haslock", "true");
    		}
    	}
    
    	// Example: Suppressing rows based on the global state
    	if(state.get("haslock") == "true") {
    		row.suppress();
    	}
    
    	// Example: Filtering and modifying data based on actual values
    	if ( row.database == "test" && row.table == "bar" ) {
    		var username = row.data.get("username");
    		if ( username == "osheroff" )
    			row.suppress();
    
    		row.data.put("username", username.toUpperCase());
    	}
    }
  5. Configure JMX for remote access

    master

    To expose JMX metrics with remote access, you must use the JAVA_OPTS environment variable before starting Maxwell.

    Below is an example configuration that allows remote access without authentication or SSL (insecure). Replace SERVER_IP_ADDRESS with your actual server IP.

    export JAVA_OPTS="-Dcom.sun.management.jmxremote \
    -Dcom.sun.management.jmxremote.port=9010 \
    -Dcom.sun.management.jmxremote.local.only=false \
    -Dcom.sun.management.jmxremote.authenticate=false \
    -Dcom.sun.management.jmxremote.ssl=false \
    -Djava.rmi.server.hostname=SERVER_IP_ADDRESS"
  6. Configure stream partitioning

    master

    Maxwell supports partitioning for Kafka, AWS Kinesis, and SNS/SQS. You can control how data is distributed using the producer_partition_by option.

    Available partitioning strategies:

    • database
    • table
    • primary_key
    • transaction_id
    • column_data
    • random

    Partitioning by Column Data: If you choose column_data, you must provide:

    1. producer_partition_columns: A comma-separated list of column names.
    2. producer_partiton_by_fallback: A fallback strategy (_database_, _table_, or _primary_key_) to use if the specified column does not exist in a row.

    Kafka Specifics: Kafka partitions are determined by HASH_FUNCTION(producer_partion_value) % TOPIC.NUMBER_OF_PARTITIONS.

    • The default HASH_FUNCTION is hashCode.
    • You can set kafka_partition_hash to murmurhash3 (seed is hardcoded to 25342).
    • Important: You should pre-create your Kafka topics with the desired number of partitions before starting Maxwell.
  7. Enable GTID-based replication

    master

    Maxwell supports GTID-based replication. To enable it, use the --gtid_mode configuration parameter.

    To support this, your MySQL server must be configured with gtid-mode=ON and enforce-gtid-consistency=true. When in GTID mode, Maxwell will transparently pick up a new replication position after a master change, though you must still re-point Maxwell to the new master (or use a floating VIP).

    [mysqld]
    server_id=1
    log-bin=master
    binlog_format=row
    gtid-mode=ON
    log-slave-updates=ON
    enforce-gtid-consistency=true
  8. Enable Row-Based Binlogs at Runtime

    master

    If binlogs are already enabled and you want to avoid a MySQL restart to configure Maxwell, you can attempt to set the binlog format and row image globally.

    Note: binlog_format is a session-based property. You must shut down all active connections for the change to row-based replication to take full effect.

    set global binlog_format=ROW;
    set global binlog_row_image=FULL;
  9. Filter tables using Basic Filters

    master

    You can configure Maxwell to include or exclude updates from specific tables using the --filter command line flag. Filters are evaluated in the order they are specified. You can use wildcards (*) and regular expressions (enclosed in /.../) to match database and table names.

    Common patterns include:

    • exclude: <pattern>: Suppress updates matching the pattern.
    • include: <pattern>: Only include updates matching the pattern.

    Note that if you use exclude: *.* followed by an include, the order matters to ensure the inclusion takes precedence.

  10. Run multiple Maxwell instances against one master

    master

    To run multiple Maxwell instances against a single master (e.g., to produce different table groups to different topics), ensure each instance has unique identifiers:

    1. client_id: Each instance must have a unique client_id to store its own unique binlog position.
    2. replica_server_id: Each instance must have a unique 32-bit integer for replica_server_id. This value must be unique across all Maxwell instances and must not conflict with any existing MySQL server_id values.