This guide will get you up and running with Apache Flink to do real-time analytics, covering some powerful features of Fluss. The guide is derived from TPC-H Q5.
For more information on working with Flink, refer to the Apache Flink Engine section.
Before proceeding with this guide, ensure that Docker and the Docker Compose plugin are installed on your machine. All commands were tested with Docker version 27.4.0 and Docker Compose version v2.30.3.
:::note We encourage you to use a recent version of Docker and Compose v2 (however, Compose v1 might work with a few adaptions). :::
We will use docker compose to spin up the required components for this tutorial.
mkdir fluss-quickstart-flink cd fluss-quickstart-flink
docker-compose.yml file with the following content:services: #begin RustFS (S3-compatible storage) rustfs: image: rustfs/rustfs:1.0.0-alpha.83 ports: - "9000:9000" - "9001:9001" environment: - RUSTFS_ACCESS_KEY=rustfsadmin - RUSTFS_SECRET_KEY=rustfsadmin - RUSTFS_CONSOLE_ENABLE=true volumes: - rustfs-data:/data command: /data rustfs-init: image: minio/mc depends_on: - rustfs entrypoint: > /bin/sh -c " until mc alias set rustfs http://rustfs:9000 rustfsadmin rustfsadmin; do echo 'Waiting for RustFS...'; sleep 1; done; mc mb --ignore-existing rustfs/fluss; " #end #begin Fluss cluster coordinator-server: image: apache/fluss:$FLUSS_DOCKER_VERSION$ command: coordinatorServer depends_on: - zookeeper - rustfs-init environment: - | FLUSS_PROPERTIES= zookeeper.address: zookeeper:2181 bind.listeners: FLUSS://coordinator-server:9123 remote.data.dir: s3://fluss/remote-data s3.endpoint: http://rustfs:9000 s3.access-key: rustfsadmin s3.secret-key: rustfsadmin s3.region: us-east-1 s3.path-style-access: true s3.assumed.role.arn: arn:aws:iam::000000000000:role/rustfsadmin s3.assumed.role.sts.endpoint: http://rustfs:9000 tablet-server: image: apache/fluss:$FLUSS_DOCKER_VERSION$ command: tabletServer depends_on: - coordinator-server environment: - | FLUSS_PROPERTIES= zookeeper.address: zookeeper:2181 bind.listeners: FLUSS://tablet-server:9123 data.dir: /tmp/fluss/data remote.data.dir: s3://fluss/remote-data s3.endpoint: http://rustfs:9000 s3.access-key: rustfsadmin s3.secret-key: rustfsadmin s3.region: us-east-1 s3.path-style-access: true s3.assumed.role.arn: arn:aws:iam::000000000000:role/rustfsadmin s3.assumed.role.sts.endpoint: http://rustfs:9000 zookeeper: restart: always image: zookeeper:3.9.2 #end #begin Flink cluster jobmanager: image: apache/fluss-quickstart-flink:$FLUSS_QUICKSTART_FLINK_DOCKER_VERSION$ ports: - "8083:8081" command: jobmanager environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager taskmanager: image: apache/fluss-quickstart-flink:$FLUSS_QUICKSTART_FLINK_DOCKER_VERSION$ depends_on: - jobmanager command: taskmanager environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager taskmanager.numberOfTaskSlots: 10 taskmanager.memory.process.size: 2048m taskmanager.memory.framework.off-heap.size: 256m sql-client: image: apache/fluss-quickstart-flink:$FLUSS_QUICKSTART_FLINK_DOCKER_VERSION$ depends_on: - jobmanager command: /opt/sql-client/sql-client environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager rest.address: jobmanager #end volumes: rustfs-data:
The Docker Compose environment consists of the following containers:
rustfsadmin/rustfsadmin. An init container (rustfs-init) automatically creates the fluss bucket on startup.CoordinatorServer, a Fluss TabletServer and a ZooKeeper server.s3.access-key and s3.secret-key. The s3.assumed.role.arn and s3.assumed.role.sts.endpoint options configure AssumeRole STS which is required by RustFS for delegation token support. Production systems should use CredentialsProvider chain specific to cloud environments.JobManager, a Flink TaskManager, and a Flink SQL client container to execute queries. The apache/fluss-quickstart-flink image bundles the Fluss Flink connector, flink-faker for demo data generation, and S3 filesystem support, so no extra jar downloads are required for this guide.:::tip RustFS is used as replacement for S3 in this quickstart example, for your production setup you may want to configure this to use cloud file system. See here for information on how to setup cloud file systems :::
docker compose up -d
This command automatically starts all the containers defined in the Docker Compose configuration in detached mode.
Run
docker compose ps
to check whether all containers are running properly.
rustfs-init service. You can access the RustFS console at http://localhost:9001 with credentials rustfsadmin/rustfsadmin to view the fluss bucket.:::note
docker compose should be executed in the created working directory that contains the docker-compose.yml file. :::Congratulations, you are all set!
First, use the following command to enter the Flink SQL CLI Container:
docker compose run sql-client
To simplify this guide, three temporary tables have been pre-created with faker connector to generate data.
You can inspect the generated source table definitions with SHOW CREATE TABLE:
SHOW CREATE TABLE `default_catalog`.`default_database`.source_order; SHOW CREATE TABLE `default_catalog`.`default_database`.source_customer; SHOW CREATE TABLE `default_catalog`.`default_database`.source_nation;
Use the following SQL to create a Fluss catalog:
CREATE CATALOG fluss_catalog WITH ( 'type' = 'fluss', 'bootstrap.servers' = 'coordinator-server:9123' );
USE CATALOG fluss_catalog;
:::info By default, catalog configurations are not persisted across Flink SQL client sessions. For further information how to store catalog configurations, see Flink's Catalog Store. :::
Running the following SQL to create Fluss tables to be used in this guide:
CREATE TABLE fluss_order ( `order_key` BIGINT, `cust_key` INT NOT NULL, `total_price` DECIMAL(15, 2), `order_date` DATE, `order_priority` STRING, `clerk` STRING, `ptime` AS PROCTIME(), PRIMARY KEY (`order_key`) NOT ENFORCED );
CREATE TABLE fluss_customer ( `cust_key` INT NOT NULL, `name` STRING, `phone` STRING, `nation_key` INT NOT NULL, `acctbal` DECIMAL(15, 2), `mktsegment` STRING, PRIMARY KEY (`cust_key`) NOT ENFORCED );
CREATE TABLE fluss_nation ( `nation_key` INT NOT NULL, `name` STRING, PRIMARY KEY (`nation_key`) NOT ENFORCED );
CREATE TABLE enriched_orders ( `order_key` BIGINT, `cust_key` INT NOT NULL, `total_price` DECIMAL(15, 2), `order_date` DATE, `order_priority` STRING, `clerk` STRING, `cust_name` STRING, `cust_phone` STRING, `cust_acctbal` DECIMAL(15, 2), `cust_mktsegment` STRING, `nation_name` STRING, PRIMARY KEY (`order_key`) NOT ENFORCED );
First, run the following SQL to sync data from source tables to Fluss tables:
EXECUTE STATEMENT SET BEGIN INSERT INTO fluss_nation SELECT * FROM `default_catalog`.`default_database`.source_nation; INSERT INTO fluss_customer SELECT * FROM `default_catalog`.`default_database`.source_customer; INSERT INTO fluss_order SELECT * FROM `default_catalog`.`default_database`.source_order; END;
Fluss primary-key tables support high QPS point lookup queries on primary keys. Performing a lookup join is really efficient and you can use it to enrich the fluss_orders table with information from the fluss_customer and fluss_nation primary-key tables.
INSERT INTO enriched_orders SELECT o.order_key, o.cust_key, o.total_price, o.order_date, o.order_priority, o.clerk, c.name, c.phone, c.acctbal, c.mktsegment, n.name FROM fluss_order o LEFT JOIN fluss_customer FOR SYSTEM_TIME AS OF `o`.`ptime` AS `c` ON o.cust_key = c.cust_key LEFT JOIN fluss_nation FOR SYSTEM_TIME AS OF `o`.`ptime` AS `n` ON c.nation_key = n.nation_key;
You can now perform real-time analytics directly on Fluss tables. For instance, to calculate the number of orders placed by a specific customer, you can execute the following SQL query to obtain instant, real-time results.
-- use tableau result mode SET 'sql-client.execution.result-mode' = 'tableau';
-- switch to batch mode SET 'execution.runtime-mode' = 'batch';
-- execute DML job synchronously SET 'table.dml-sync' = 'true';
-- use limit to query the enriched_orders table SELECT * FROM enriched_orders LIMIT 2;
Sample Output
+-----------+----------+-------------+------------+----------------+--------+------------+----------------+--------------+-----------------+-------------+ | order_key | cust_key | total_price | order_date | order_priority | clerk | cust_name | cust_phone | cust_acctbal | cust_mktsegment | nation_name | +-----------+----------+-------------+------------+----------------+--------+------------+----------------+--------------+-----------------+-------------+ | 23199744 | 9 | 266.44 | 2024-08-29 | high | Clerk1 | Joe King | 908.207.8513 | 124.28 | FURNITURE | JORDAN | | 10715776 | 2 | 924.43 | 2024-11-04 | medium | Clerk3 | Rita Booke | (925) 775-0717 | 172.39 | FURNITURE | UNITED | +-----------+----------+-------------+------------+----------------+--------+------------+----------------+--------------+-----------------+-------------+
To quickly get the total number of rows in a table, you can use COUNT(*):
-- count total rows in the table SELECT COUNT(*) FROM enriched_orders;
Sample Output
+--------+ | EXPR$0 | +--------+ | 200 | +--------+
You can execute this COUNT(*) query multiple times. Because Fluss ingests data continuously in real time, the result will reflect the latest row count and may increase with each execution, but the max count value should be 10000 as the total number of the source_order source is 10000 rows. The query should return very quickly, as Fluss maintains table-level statistics that enable efficient aggregation without scanning the entire dataset.
If you are interested in a specific customer, you can retrieve their details by performing a lookup on the cust_key.
-- lookup by primary key SELECT * FROM fluss_customer WHERE `cust_key` = 1;
Sample Output
+----------+---------------+--------------+------------+---------+------------+ | cust_key | name | phone | nation_key | acctbal | mktsegment | +----------+---------------+--------------+------------+---------+------------+ | 1 | Al K. Seltzer | 817-617-7960 | 1 | 533.41 | AUTOMOBILE | +----------+---------------+--------------+------------+---------+------------+
Note: Overall the query results are returned really fast, as Fluss enables efficient primary key lookups for tables with defined primary keys.
You can use UPDATE and DELETE statements to update/delete rows on Fluss tables.
-- update by primary key UPDATE fluss_customer SET `name` = 'fluss_updated' WHERE `cust_key` = 1;
Then you can lookup the specific row:
SELECT * FROM fluss_customer WHERE `cust_key` = 1;
Sample Output
+----------+---------------+--------------+------------+---------+------------+ | cust_key | name | phone | nation_key | acctbal | mktsegment | +----------+---------------+--------------+------------+---------+------------+ | 1 | fluss_updated | 817-617-7960 | 1 | 533.41 | AUTOMOBILE | +----------+---------------+--------------+------------+---------+------------+
Notice that the name column has been updated to fluss_updated.
DELETE FROM fluss_customer WHERE `cust_key` = 1;
The following SQL query should return an empty result.
SELECT * FROM fluss_customer WHERE `cust_key` = 1;
The following command allows you to quit Flink SQL Client.
quit;
You can visit http://localhost:9001/ and sign in with rustfsadmin / rustfsadmin to view the files stored on remote storage.
After finishing the tutorial, run exit to exit Flink SQL CLI Container and then run
docker compose down -v
to stop all containers.
Now that you're up and running with Fluss and Flink, check out the Apache Flink Engine docs to learn more features with Flink or this guide to learn how to set up an observability stack for Fluss and Flink.