Flink Connector Redis

repository·main·Indexed 42 days ago

https://github.com/jeff-zou/flink-connector-redis

A high-performance Flink connector for Redis enhanced from the Apache Bahir project. It utilizes Lettuce for asynchronous I/O and supports Table/SQL API, dimension table join caching, and Flink CDC deletes. The connector provides support for single, cluster, and sentinel Redis modes, and allows for flexible data mapping using 'column' or 'row' structures.

Tokens
9.9K
Snippets
17
Records
28
Agent score
43%

What's inside Flink Connector Redis

  1. Configure Redis Sink Data Structures

    main

    You can control how Flink data is mapped to Redis values using the value.data.structure parameter in your DDL.

    value.data.structure = 'column' (Default)

    In this mode, the key is determined by the order of fields in the DDL. There is no explicit mapping of a primary key to a Redis key; instead, fields are mapped to specific parts of the Redis command.

    Example (SET):

    -- username is the key, passport is the value
    CREATE TABLE sink_redis(username VARCHAR, passport VARCHAR) WITH ('command'='set');

    Example (HSET):

    -- name is the map key, subject is the field, score is the value
    CREATE TABLE sink_redis(name VARCHAR, subject VARCHAR, score VARCHAR) WITH ('command'='hset');

    value.data.structure = 'row'

    In this mode, the entire row is taken as the value, with fields separated by the \01 delimiter.

    Example (SET):

    CREATE TABLE sink_redis(username VARCHAR, passport VARCHAR) WITH ('command'='set');
    -- Key: username, Value: username\01passport

    Example (HSET):

    CREATE TABLE sink_redis(name VARCHAR, subject VARCHAR, score VARCHAR) WITH ('command'='hset');
    -- Key: name, Field: subject, Value: name\01subject\01score
    create table sink_redis(username VARCHAR, passport VARCHAR)  with ('command'='set') 
    
    create table sink_redis(name VARCHAR, subject VARCHAR, score VARCHAR)  with ('command'='hset') 
    
    create table sink_redis(username VARCHAR, passport VARCHAR) with ('command'='set') 
    #key: username , value: username\01passport.
    
    create table sink_redis(name VARCHAR, subject VARCHAR, score VARCHAR) 
    key: name, field:subject, value: name\01subject\01score.
  2. Configure data structure storage (column vs row)

    main

    The value.data.structure parameter controls how data is mapped to Redis values.

    • column (default): Maps fields directly to Redis values based on DDL order.

      • For set: The first field is the Key, the second is the Value.
      • For hset: The first field is the Key, the second is the Hash Field, the third is the Value.
    • row: Serializes the entire row into a single Redis value, with fields separated by the \01 delimiter.

      • For set: Key is the first field, Value is field1\01field2....
      • For hset: Key is the first field, Hash Field is the second, Value is field1\01field2....
  3. Perform multi-field dimension table joins using 'row' structure

    main

    By default, Redis stores values as single strings. To store and retrieve multiple fields for a single key, set 'value.data.structure'='row'. This stores the entire row in the Redis value, with fields separated by the \01 (SOH) character.

    When reading back with 'command'='get', the connector will parse this delimited string back into the specified Flink table columns.

    -- Write multiple fields as a single row in Redis
    create table sink_redis(uid VARCHAR, score double, score2 double) 
    with (
      'connector'='redis',
      'host'='10.11.69.176',
      'port'='6379',
      'redis-mode'='single',
      'password'='****',
      'command'='SET',
      'value.data.structure'='row'
    );
    
    insert into sink_redis select * from (values ('1', 10.3, 10.1));
    -- Redis value becomes: "1\x0110.3\x0110.1"
    
    -- Read the row back using 'get'
    create table join_table 
    with (
      'command'='get', 
      'value.data.structure'='row'
    ) like sink_redis;
    
    -- Join as usual
    select s.uid, j.score, j.score2
    from source_table as s
    join join_table for system_time as of s.proc_time as j on j.uid = s.uid;
  4. Use the connector with Flink 1.12

    main

    For Flink 1.12 compatibility, you must use the specific version branch flink-1.12 which utilizes Jedis instead of Lettuce.

    Add the following dependency to your pom.xml:

    <dependency>
        <groupId>io.github.jeff-zou</groupId>
        <artifactId>flink-connector-redis</artifactId>
        <version>1.1.1-1.12</version>
    </dependency>
  5. Install the Flink Redis Connector

    main

    To use the connector in your Flink project, you can either import the JAR file into your Flink lib directory or add it as a Maven dependency.

    Maven Dependency

    If you are using Maven, add the following dependency to your pom.xml. Note that if you do not include the jar-with-dependencies classifier, you must manually provide the Lettuce and netty-transport-native-epoll dependencies.

    <dependency>
      <groupId>io.github.jeff-zou</groupId>
      <artifactId>flink-connector-redis</artifactId>
      <version>1.4.3</version>
      <!-- Uncomment the line below if Lettuce dependencies are not provided elsewhere -->
      <!-- <classifier>jar-with-dependencies</classifier> -->
    </dependency>

    Manual Installation

    1. Run mvn package to generate the JAR.
    2. Import the generated flink-connector-redis-1.4.3.jar into your Flink lib folder.
    3. If the standard JAR is used, ensure Lettuce(6.2.1) and netty-transport-native-epoll(4.1.82.Final) are available in the classpath. Alternatively, use flink-connector-redis-1.4.3-jar-with-dependencies.jar to include all dependencies.
    <dependency>
      <groupId>io.github.jeff-zou</groupId>
      <artifactId>flink-connector-redis</artifactId>
      <version>1.4.3</version>
      <!-- When the Lettuce netty-transport-native-epoll dependency is not imported separately -->
        <!--            <classifier>jar-with-dependencies</classifier>-->
    </dependency>
  6. Configure Redis connection and mode

    main

    Use these parameters to establish the connection to your Redis instance.

    ParameterDefaultTypeDescription
    connector(none)StringMust be redis
    host(none)StringRedis IP
    port6379IntegerRedis port
    passwordnullStringRedis password
    database0IntegerRedis database index
    timeout2000IntegerConnection timeout in ms
    redis-mode(none)IntegerMode: single, cluster, or sentinel
    cluster-nodes(none)StringComma-separated list of ip:port for cluster mode (e.g., 10.11.80.147:7000,10.11.80.147:7001)
  7. Limit Sink resources for online SQL debugging

    main

    When debugging SQL online, use these parameters to prevent the sink from consuming excessive resources:

    ParameterDefaultTypeDescription
    sink.limitfalseBooleanEnables resource limiting
    sink.limit.max-num10000IntegerMaximum data volume per slot in the TaskManager
    sink.limit.interval100StringInterval between writes per slot (in milliseconds)
    sink.limit.max-online1800000LongMaximum online time per slot in milliseconds (default 30 mins)
  8. Configure Redis Connector Parameters

    main

    The following parameters can be used in the WITH clause of your Flink SQL DDL to configure the Redis connector.

    Connection Settings

    FieldDefaultTypeDescription
    connector(none)StringMust be redis
    host(none)StringRedis IP
    port6379IntegerRedis port
    passwordnullStringRedis password
    database0IntegerRedis database index
    timeout2000IntegerConnection timeout in ms
    cluster-nodes(none)StringComma-separated ip:port list for cluster mode
    redis-mode(none)IntegerMode type (e.g., single, cluster)

    Lookup (Dimension Table) Settings

    FieldDefaultTypeDescription
    lookup.cache.max-rows-1IntegerQuery cache size (reduce duplicate queries)
    lookup.cache.ttl-1IntegerCache expiration time in seconds
    lookup.max-retries1IntegerRetries on failed queries
    lookup.cache.load-allfalseBooleanIf hget, query all elements from map to cache to prevent cache penetration

    Sink & TTL Settings

    FieldDefaultTypeDescription
    sink.max-retries1IntegerRetries for write failures
    ttl(none)IntegerKey expiration time in seconds
    ttl.on.time(none)StringExpiration time in LocalTime.toString() format (e.g., 10:00:00)
    ttl.key.not.absentfalseBooleanSet TTL only when the key doesn't exist

    Debugging & Resource Limiting

    FieldDefaultTypeDescription
    sink.limitfalseBooleanEnable sink resource limiting
    sink.limit.max-num10000IntegerMax writes per thread
    sink.limit.interval100StringMillisecond interval between writes per thread
    sink.limit.max-online30*60*1000LLongMax online milliseconds per thread
  9. Configure Redis Sentinel connection parameters

    main

    When using a Redis Sentinel cluster, you must provide additional connection parameters in your Flink configuration:

    FieldDefaultTypeDescription
    master.name(none)StringThe name of the master node
    sentinels.info(none)StringA comma-separated list of sentinel addresses (e.g., 10.11.80.147:7000,10.11.80.147:7001)
    sentinels.password(none)StringThe password for the sentinel process
  10. Configure Lookup Cache for dimension tables

    main

    To reduce redundant Redis queries during joins, you can enable a lookup cache using these parameters:

    ParameterDefaultTypeDescription
    lookup.cache.max-rows-1IntegerMaximum number of rows to cache. Set to > -1 to enable
    lookup.cache.ttl-1IntegerCache expiration time in seconds. Must be > -1 to enable
    lookup.cache.load-allfalseBooleanIf true, for hget commands, it fetches all elements from the Redis map into the cache to prevent cache penetration.
  11. Configure Redis TTL (Time To Live)

    main

    Set expiration for keys during sink operations using these parameters:

    ParameterDefaultTypeDescription
    ttl(none)IntegerKey expiration time in seconds. Applied during every sink operation
    ttl.on.time(none)StringSpecific expiration time (e.g., 10:00 or 12:12:01). Only used if ttl is not configured
    ttl.key.not.absentfalseBooleanIf true, the TTL is only set if the key does not already exist.