FlinkSQL没有做热身准备,上手开发你心中有底吗?

Flink 官网table api例子异常排查 下载的flink 1.13.1版本,scala 2.11版本 按照官网,https://ci.apache.org/projects/flink/flink-docs-release-1.13/docs/try-flink/table_api/ 下载https://github.com/apache/flink-playgrounds这个代码 第一个异常: Error: A JNI error has occurred, please check your installation and . 阅读详情

 

前言

在使用FlinkSQL考虑到做指标统计,心中疑问好多:

状态能恢复吗?

状态数据越来越大怎么办,怎么去清除之前数据?

我怎么按天去统计数据?

如果遇到主表要join很早的数据怎么办?......

心中想到这些问题,一万个xxx而过。所以需要做个热身准备,不然真的不敢上手开发业务。

  1. 验证状态数据恢复

  2. 验证状态的清除策略与时效

  3. 验证按天计算数据

  4. 遇到Join表怎么办?(其他方案替代)

  5. 「附带验证过程的」Bug记录

如果其他疑点需要验证或者调研的,可以评论或者留言给我。欢迎转发给需要的朋友......

Flink环境配置

def getTable(): StreamTableEnvironment ={
    val bsEnv: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    bsEnv.enableCheckpointing(10000,CheckpointingMode.EXACTLY_ONCE)
    bsEnv.getCheckpointConfig.setCheckpointTimeout(60000)
    bsEnv.getCheckpointConfig.setMaxConcurrentCheckpoints(1)
    bsEnv.getCheckpointConfig.enableExternalizedCheckpoints(CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION)
    bsEnv.setStateBackend(new EmbeddedRocksDBStateBackend)
    val dir ="file:///D:/hdfs/
    bsEnv.getCheckpointConfig.setCheckpointStorage(dir)
    val bsSettings: EnvironmentSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
    val bsTableEnv: StreamTableEnvironment = StreamTableEnvironment.create(bsEnv, bsSettings)
    bsTableEnv
  }

主体逻辑

从source kafka 到 sink kafka:做简单的pv和uv的

 val source =
      s"""
         |CREATE TABLE pageviews  (
         | user_id BIGINT,
         |  page_id BIGINT,
         |  user_region STRING
         |) WITH (
         |    'connector' = 'kafka',
         |    'topic' = 'testtopic',
         |    'properties.bootstrap.servers' = 'devcdh1:9092,devcdh2:9092,devcdh3:9092',
         |    'format' = 'json'
         |)
       """.stripMargin
       
    val sink =
      s"""
         |CREATE TABLE pageviews_per_region (
         | user_region STRING,
         |  pv BIGINT,
         |  uv BIGINT,
         |  PRIMARY KEY (user_region) NOT ENFORCED
         |) WITH (
         | 'connector' = 'upsert-kafka',
         | 'topic' = 'test',
         | 'properties.bootstrap.servers' = 'xxxx',
         | 'properties.group.id' = 'test',
         |  'key.format' = 'json',
         |  'value.format' = 'json'
         |)
       """.stripMargin
    tableEnv.executeSql(source)
    tableEnv.executeSql(sink)
    val insert =
      s"""
         |INSERT INTO pageviews_per_region
         |SELECT
         |  user_region,
         |  COUNT(*),
         |  COUNT(DISTINCT user_id)
         |FROM pageviews
         |GROUP BY user_region
       """.stripMargin
    tableEnv.executeSql(insert)
    

1. 验证状态数据恢复

往期对状态的详细解读,这里不再细说:

Flink的Checkpoint机制详解

Flink的一致性保证

source数据

>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U003"}
>{"user_id":1002,"page_id":1002,"user_region":"U003"}
>{"user_id":1002,"page_id":1002,"user_region":"U003"}
>{"user_id":1002,"page_id":1002,"user_region":"U003"}
>{"user_id":1002,"page_id":1002,"user_region":"U003"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}
>{"user_id":1002,"page_id":1002,"user_region":"U002"}

sink结果

kill前的
{"user_region":"U002","pv":1,"uv":1}
{"user_region":"U002","pv":2,"uv":1}
{"user_region":"U002","pv":3,"uv":1}
{"user_region":"U002","pv":1,"uv":1}
{"user_region":"U002","pv":2,"uv":1}
{"user_region":"U003","pv":1,"uv":1}
{"user_region":"U003","pv":2,"uv":1}
{"user_region":"U003","pv":3,"uv":1}
{"user_region":"U003","pv":4,"uv":1}
{"user_region":"U003","pv":5,"uv":1}
{"user_region":"U002","pv":3,"uv":1}

恢复后的

{"user_region":"U002","pv":4,"uv":1}
{"user_region":"U002","pv":5,"uv":1}

操作

第一次启动
flink run  -m yarn-cluster  -ys 1 -p 1 -ynm flink-sql /data/flink_jars/indicator-sql.jar

kill后的再启动

flink run -s hdfs://nameservice//user/flink/checkpoint/8d80fe1615666f65342b2348d3c39044/chk-18/_metadata -m yarn-cluster  -ys 1 -p 1 -ynm flink-sql /data/flink_jars/indicator-sql.jar

2. 验证状态的清除(如何限制state)

「说明」:实时计算不断地去充实内存保存状态,达到一定量的时候肯定撑不住,现需要定期去清除状态。

TableEnvironmentTableConfig tConfig = tableEnv.getConfig();

tConfig.setIdleStateRetentionTime(Time.hours(5), Time.hours(10));

对setIdleStateRetentionTime两个参数的理解

这两个参数的含义:

minIdleStateRetentionTime: key被移除前state最少的空闲时间;

maxIdleStateRetentionTime:key被移除前state最长的空闲时间。


第一次:我们假设在时间为0的时候来了一条数据,那么会根据这个数据的key以及时当前系统时间为其注册一个Timer,这个Timer是用来清除state的,那么注册的Timer的时间是什么呢?

long cleanupTime = currentTime + maxRetentionTime;

其实Timer的时间就是 0 + 10 =10,也就是在10min后会回调相应的方法,这个方法主要就是用来清除state的。


第二次:在时间为2的时候来了第二条数据,这个数据的key与第一个一样,那么会操作同一个state,那么这个数据是否会去注册Timer吗?

答案是肯定会去注册Timer,只不过引入了一个if条件,只有满足这个条件才会去把之前的Timer删掉,然后重新注册一个Timer,否则维持之前的Timer不变。

(currentTime + minRetentionTime) > curCleanupTime

currentTime = 2

minRetentionTime = 5

curCleanupTime = 10

curCleanupTime我们可以理解为当前这个key对应的Timer的时间,也就是第一条数据注册的Timer的时间 10,因为2 + 5 > 10 条件不成立,所以不会对这个key的Timer做任何操作。


第三次:在时间为10的时候来了第三条数据,并且key与前两个数据一样,我们看一下此时条件是否成立吗?

currentTime = 10

minRetentionTime = 5

curCleanupTime = 10

那么10 +5  > 10 成立,会对这个key的Timer进行更新,把之前的10的Timer删掉,然后重新注册一个Timer时间是

currentTime = 10

maxRetentionTime = 10

所以最新的Timer时间 = 10 + 10= 20


第四次:继续,第四条数据在当前时间是12到来,12 + 5  > 20不成立,不去做任和Timer的更新操作。


第五次:继续,第五条相同key的数据到来,此时时间是16,但是在20的时候这个key的Timer执行了回调,进行了state的清除操作,所以这条数据已经找不到之前的state了,相当于重新开始,创建state,然后去注册Timer,Timer的时间是20 + 10 = 30。


对上面几次小结:通过上面可以发现,第3条数据到来时间是10,第四条数据到来时间是12,然后20min时候state清除,所以这个state的空闲时间是 20 - 12 =8,我们可以说这个state空闲了8,然后被删除,完全符合最少5,最长10的定义。

我们假设第4条数据没来过,那么第3条数据到来时间是10,然后20时候state被清除,期间一直无这个key的数据,所以state存活时间是10,同样完全符合我们定义的最小时间5以及最大时间10的范围。


「源码」

public void setIdleStateRetentionTime(Time minTime, Time maxTime) {
    if (maxTime.toMilliseconds() - minTime.toMilliseconds() >= 300000L || maxTime.toMilliseconds() == 0L && minTime.toMilliseconds() == 0L) {
        this.minIdleStateRetentionTime = minTime.toMilliseconds();
        this.maxIdleStateRetentionTime = maxTime.toMilliseconds();
    } else {
        throw new IllegalArgumentException("Difference between minTime: " + minTime.toString() + " and maxTime: " + maxTime.toString() + "shoud be at least 5 minutes.");
    }
}


public interface CleanupState {
    default void registerProcessingCleanupTimer(
            ValueState<Long> cleanupTimeState,
            long currentTime,
            long minRetentionTime,
            long maxRetentionTime,
            TimerService timerService)
            throws Exception {
        // last registered timer
        Long curCleanupTime = cleanupTimeState.value();
 
        // check if a cleanup timer is registered and
        // that the current cleanup timer won't delete state we need to keep
        if (curCleanupTime == null || (currentTime + minRetentionTime) > curCleanupTime) {
            // we need to register a new (later) timer
            long cleanupTime = currentTime + maxRetentionTime;
            // register timer and remember clean-up time
            timerService.registerProcessingTimeTimer(cleanupTime);
            // delete expired timer
            if (curCleanupTime != null) {
                timerService.deleteProcessingTimeTimer(curCleanupTime);
            }
            cleanupTimeState.update(cleanupTime);
        }
    }
}

「总结」

对于上面,最好的理解是:

「最长体现在」: 如果在0的时间进来一条数据,然后10内没有数据进来,这个时候会触发清除操作,也就是所谓的key被移除前state最长的空闲时间。

「最短体现在」:第一次进来数据时间是5.1(刚过5),5+5.1>10成立,Timer的时间更新为15.1,这个时候闲置5min+,即5.1+5+5+ >15.1 然后被清除,也就是所谓的key被移除前state最短的空闲时间。


验证

setIdleStateRetentionTime 方法在此版本已经过时,新版本已经没有maxIdleStateRetentionTime,变为取决于minIdleStateRetentionTime

    @Deprecated
    public void setIdleStateRetentionTime(Time minTime, Time maxTime) {
        if (maxTime.toMilliseconds() - minTime.toMilliseconds() >= 300000L || maxTime.toMilliseconds() == 0L && minTime.toMilliseconds() == 0L) {
            this.setIdleStateRetention(Duration.ofMillis(minTime.toMilliseconds()));
        } else {
            throw new IllegalArgumentException("Difference between minTime: " + minTime.toString() + " and maxTime: " + maxTime.toString() + " should be at least 5 minutes.");
        }
    }
    
    @Deprecated
    public long getMaxIdleStateRetentionTime() {
        return this.getMinIdleStateRetentionTime() * 3L / 2L;
    }
    
    public void setIdleStateRetention(Duration duration) {
        this.configuration.set(ExecutionConfigOptions.IDLE_STATE_RETENTION, duration);
    }
   

所以,用setIdleStateRetention方法即可,原理和上面说的一样

    bsTableEnv
        .getConfig
        .setIdleStateRetention(
            Duration.ofMillis( 
                Time.minutes(1L).toMilliseconds
                )
        )

「首次输入一批数据」

{"user_id":1002,"page_id":1001,"user_region":"U001"}

{"user_id":1001,"page_id":1002,"user_region":"U001"}

{"user_id":1002,"page_id":1001,"user_region":"U002"}

{"user_id":1001,"page_id":1002,"user_region":"U001"}

{"user_id":1002,"page_id":1002,"user_region":"U001"}

「首次结果」

{"user_region":"U001","pv":1,"uv":1}
{"user_region":"U001","pv":2,"uv":1}
{"user_region":"U002","pv":1,"uv":1}
{"user_region":"U001","pv":3,"uv":2}
{"user_region":"U001","pv":4,"uv":2}

「一分钟后再次输入数据」

{"user_id":1001,"page_id":1002,"user_region":"U001"}
{"user_id":1002,"page_id":1002,"user_region":"U001"}

「一分钟后再次输入数据后的结果(从头计算)」

{"user_region":"U001","pv":1,"uv":1}
{"user_region":"U001","pv":2,"uv":2}

「验证状态的清除成功」


3. 验证按天计算数据

「逻辑SQL」

 val source =
      s"""
         |CREATE TABLE pageviews  (
         |  user_id BIGINT,
         |  page_id BIGINT,
         |  user_region STRING,
         |  app_time BIGINT,
         |  dt AS FROM_UNIXTIME(app_time / 1000, 'yyyy-MM-dd')
         |) WITH (
         |    'connector' = 'kafka',
         |    'topic' = 'testtopic',
         |    'properties.bootstrap.servers' = 'xxxx',
         |    'format' = 'json'
         |)
       """.stripMargin

    val sink =
      s"""
         |CREATE TABLE pageviews_per_region (
         |  dt STRING,
         |  user_region STRING,
         |  pv BIGINT,
         |  uv BIGINT,
         |  PRIMARY KEY (user_region) NOT ENFORCED
         |) WITH (
         | 'connector' = 'upsert-kafka',
         | 'topic' = 'test',
         | 'properties.bootstrap.servers' = 'xxxx',
         | 'properties.group.id' = 'test',
         |  'key.format' = 'json',
         |  'value.format' = 'json'
         |)
       """.stripMargin
    tableEnv.executeSql(source)
    tableEnv.executeSql(sink)

    val insert =
      s"""
         |INSERT INTO pageviews_per_region
         |SELECT
         |  dt,
         |  user_region,
         |  COUNT(*),
         |  COUNT(DISTINCT user_id)
         |FROM pageviews
         |GROUP BY dt,user_region
       """.stripMargin
    tableEnv.executeSql(insert)

「测试数据」

2021-08-04数据

>{"user_id":1002,"page_id":1002,"user_region":"U001","app_time":1628061421071}

>{"user_id":1002,"page_id":1002,"user_region":"U001","app_time":1628061421071}

>{"user_id":1002,"page_id":1002,"user_region":"U001","app_time":1628006400000}

2021-08-03数据

>{"user_id":1002,"page_id":1002,"user_region":"U001","app_time":1627920000000}

>{"user_id":1002,"page_id":1002,"user_region":"U001","app_time":1627920000000}

「测试结果」

{"dt":"2021-08-04","user_region":"U001","pv":1,"uv":1}

{"dt":"2021-08-04","user_region":"U001","pv":2,"uv":1}

{"dt":"2021-08-04","user_region":"U001","pv":3,"uv":1}


{"dt":"2021-08-03","user_region":"U001","pv":1,"uv":1}

{"dt":"2021-08-03","user_region":"U001","pv":2,"uv":1}


4. 替代join方式

「方案一 UDF」

先实现一个简单的

class JoinFunction extends ScalarFunction {
 def eval(str:String): String ={
   str+"_hello"
 }

}
tableEnv.createTemporarySystemFunction("test_function",new JoinFunction())

val sql =
      s"""

         |SELECT
         |  dt,
         |  test_function(user_region),
         |  COUNT(*),
         |  COUNT(DISTINCT user_id)
         |FROM pageviews
         |GROUP BY dt,user_region
       """.stripMargin  
tableEnv.executeSql(sql).print()
+----+--------------------------------+--------------------------------+----------------------+----------------------+
| op |                             dt |                         EXPR$1 |               EXPR$2 |               EXPR$3 |
+----+--------------------------------+--------------------------------+----------------------+----------------------+
| +I |                     2021-08-03 |                       U001_hello |                    1 |                    1 |

去库里查找(类join取值)

class JoinFunction extends ScalarFunction {
 def eval(column:String): String ={
   println(s"进来的参数:column:${column}")
   val sql=s"select app_code from default.test_flink where order_number='$column'"
   val maps: util.List[util.Map[String, AnyRef]] = ImpalaDevUtil.executeQuery(sql, null)
   println(maps)
   println(!maps.isEmpty && maps.size()>0)
   if (!maps.isEmpty && maps.size()>0) {
     val app_code: String = maps.get(0).get("app_code").toString
     println(s"获取的值:$app_code")
     return app_code
   }
   null
 }

}

注册函数与逻辑

tableEnv.createTemporarySystemFunction("join_function",new JoinFunction())
 """
     |SELECT
     |  dt,
     |  join_function(user_region) appcode,
     |  COUNT(*),
     |  COUNT(DISTINCT user_id)
     |FROM pageviews
     |GROUP BY dt,user_region
""".stripMargin

数据

{"user_id":1002,"page_id":1002,"user_region":"1556666","app_time":1627920000000}

结果

+----+--------------------------------+--------------------------------+----------------------+----------------------+
| op |                             dt |                        appcode |               EXPR$2 |               EXPR$3 |
+----+--------------------------------+--------------------------------+----------------------+----------------------+
| +I |                     2021-08-03 |                       25656660 |                    1 |                    1 |

方案2:实现其他库的connectors(可以用Join)

JDBC 现支持Derby,MySQL,Postgres,对于不同的数据源需要自定义:比如Impala

1. ImpalaRowConverter 修改converterName即可

public class ImpalaRowConverter extends AbstractJdbcRowConverter {
    private static final long serialVersionUID = 1L;

    public String converterName() {
        return "Impala";
    }

    public ImpalaRowConverter(RowType rowType) {
        super(rowType);
    }
}

2. 实现 ImpalaDialect

并重写getUpsertStatement 或者直接复制MySQLDialect微修改就可以

ImpalaDialect extends AbstractDialect

3. 增添Impala

public final class JdbcDialects {

 private static final List<JdbcDialect> DIALECTS = Arrays.asList(new DerbyDialect(), new MySQLDialect(), new PostgresDialect());
}

4. 重新打包flink-connector-jdbc替换Flink集群/lib下面原生的jar包


5. Bug记录

FlinkSQL 找不到 kafka connector

Caused by: org.apache.flink.table.api.ValidationException: Cannot discover a connector using option: 'connector'='kafka'
        at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:467)
        at org.apache.flink.table.factories.FactoryUtil.getDynamicTableFactory(FactoryUtil.java:441)
        at org.apache.flink.table.factories.FactoryUtil.createTableSource(FactoryUtil.java:133)
        ... 53 more

Caused by: org.apache.flink.table.api.ValidationException: Could not find any factory for identifier 'kafka' that implements 'org.apache.flink.table.factories.DynamicTableFactory' in the classpath.

Available factory identifiers are:

blackhole
datagen
filesystem

解决:下载到lib包下

<!-- https://mvnrepository.com/artifact/org.apache.flink/flink-sql-connector-kafka -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-sql-connector-kafka_2.11</artifactId>
    <version>1.13.1</version>
    <scope>provided</scope>
</dependency>

kafka目前只能写入append的数据

TableSink doesn't support consuming update changes which is produced by node GroupAggregate

解决:这个是正常现象。如果你用了普通的group by的话,那么它的结果就是有更新的,所以需要sink支持写入update的结果, 但是kafka目前只能写入append的数据,所以会报上面的错误。

  • 使用upsert-kafka

WITH (
'connector' = 'upsert-kafka',
'topic' = 'test',
'properties.bootstrap.servers' = 'xxxx',
'properties.group.id' = 'test',
 'key.format' = 'json',
 'value.format' = 'json'
)
  • 或者使用window group


总结

通过以上验证,最起码前期准备工作已经做完,可以用SQL去开发业务了。其他性能问题或者业务复杂度问题根据情况去解决。

如果其他疑点需要验证或者调研的,可以评论或者留言给我......


 

 

大数据左右手

技术如同手中的水有了生命似的,汇聚在了一起。作为大数据开发工作者,致力于大数据技术的学习与工作,分享大数据原理、架构、实时、离线、面试与总结,分享生活思考与读书见解。总有适合你的那一篇。

关注公众号!!!

和我联系吧,加群交流大数据知识,一起成长~~~

Flink-SQL源码解读(一)window算子的创建的源码分析 本文大体框架参考 https://blog.youkuaiyun.com/LS_ice/article/details/90711744 flink版本:1.9 Intro 作为无限流的核心机制,流可以分割为大小有限的窗口,通过在窗口内进行聚合。把源源不断产生的数据根据不同的条件划分成一段一段有边界的数据区间,使用户能够利用窗口功能实现很多复杂的统计分析需求。 windowOperator算子的创建架构 w... 阅读详情

相关推荐

Flink源码分析--(二)FLink SQL Window功能

文章目录一、window功能概述二、window分类及整体流程三、 创建WindowOperator算子四、WindowOperator处理数据 一、window功能概述 Flink窗口具有多种类型,包括基于计数的滑动窗口和滚动窗口,基于系统时间和事件时间的滑动窗口、滚动窗口以及会话窗口,从而把源源不断产生的数据根据不同的条件划分成一段一段有边界的数据区间,使用户能够利用窗口功能实现很多复杂的统...

LS_ice的专栏 5900

flinksql获取系统当前时间搓_Flink SQL内的状态清理

01背景在Flink SQL内部进行Query操作时候,无论是unbounded还是bounded数据,TableAPI与SQL保持了一致的语义。在大多数场景下,实时计算的结果与离线的结果可以保持一致。但是因为实时数据是unbounded的,Flink内会为每个key保存相应的State,随着时间的增长以及key的增加,面临超出存储的风险越来越大。在一些continuous queri...

weixin_31006689的博客 1029

【FlinkSQL】ValidationException: Could not find any factory for identifier ‘kafka‘

flinksql程序编写create语句遇到下面问题 Caused by: org.apache.flink.table.api.ValidationException: Cannot discover a connector using option: 'connector'='kafka' at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:367) at

一杯咖啡半杯糖的博客 2935

Flink SQL空闲状态保留时间(idle state retention time)实现原理

前言 如果要列举Flink SQL新手有可能犯的错误,笔者认为其中之一就是忘记设置空闲状态保留时间导致状态爆炸。2021年的第一篇技术文,时间很紧张,聊聊这个简单的话题吧。 为什么要设置 如果我们在数据流上进行分组查询,分组处理产生的结果(不仅仅是聚合结果)会作为中间状态存储下来。随着分组key的不断增加,状态自然也会不断膨胀。但是这些状态数据基本都有时效性,不必永久保留。例如,使用Top-N语法...

LittleMagic's Blog 5896

StateTtlConfig配置项

StateTtlConfig ttlConfig = StateTtlConfig .newBuilder(org.apache.flink.api.common.time.Time.seconds(1)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setState

u011624157的专栏 2812

全国大学校园网—拓扑图欣赏

全国大学网络拓扑图(一百张) 典型十张 1、 中央财经大学 2、国防科技大学 3、济南大学 4、西安石油大学 5、南通大学IPV6网络 6、中国矿业大学 7、青海师范大学 8、内蒙古科技大学 9、三峡大学 10、中共江苏省党校 剩余90张 点赞、评论获取 ...

LiBai'S BLOG 5万+

flink创建kafka流表时报错:Cannot discover a connector using option: ‘connector‘=‘kafka‘

flink创建kafka表时,报错: Cannot discover a connector using option: 'connector'='kafka' Could not find any factory for identifier 'kafka' that implements 'org.apache.flink.table.factories 原因:参数写错。下方参数提供了可用参数: 解决: 修改参数设置为 'connector' = 'kafka-0.11...

东城庞太师 6475

python操作git推送自己分支并合并主分支(持续优化该工具)

python git

qq_44707265的博客 863

Flink sql withIdleStateRetentionTime源码

Flink1.8.0版本。好奇心来自Flink sql设置withIdleStateRetentionTime的最小和最大时间后,状态是如何删除的。 qConfig.withIdleStateRetentionTime( Time.hours(24), Time.hours(48)); 点击withIdleSta...

文报的博客 1295

保姆级教程:在SIwave 2024 R2中搞定TDR仿真,手把手教你分析USB 3.0差分线阻抗

本文提供了一份详细的SIwave 2024 R2 TDR仿真教程,重点讲解如何分析USB 3.0差分线阻抗。从项目准备、参数配置到结果分析,逐步指导工程师完成完整的仿真流程,并分享实战经验和高级技巧,帮助提升信号完整性分析能力。

weixin_33670786的博客 498

相控阵雷达“相控”原理---多张动画示意图完美解释

很容易就能发现,相控阵雷达的天线由无数个小单元天线组成,这些小单元天线叫做“阵元”,对于有源相控阵雷达来说,每一个阵元都是独立控制的,它们既能独立发射雷达波,也能接收雷达波的回波信号。相控阵雷达可以在1秒内关机,1秒内开机,好处是,当军舰遇到依靠雷达信号进行引导的“反雷达导弹”时,可以迅速地把朝向来袭导弹方向的雷达关机,同时,其他方向的雷达保持开启。在过去,军舰上安装有不同种类的雷达,体积庞大、重不说,另一个麻烦是干扰,常常是,一种雷达工作时,另一种雷达就会受到干扰,严重的甚至不能同时工作。

muyangjun的专栏 1万+

execute成功与否判断

为什么80%的码农都做不了架构师?>>> ...

weixin_34267123的博客 1626

DPABINet保姆级教程:零代码完成脑网络分析与图论计算

本文提供DPABINet的零代码脑网络分析全流程指南。该工具通过图形化界面集成时间序列提取、连接矩阵构建、图论指标计算及统计分析,大幅降低技术门槛。重点解析了阈值选择、二值化与小世界属性计算等关键步骤,帮助研究者快速掌握脑网络分析与图论计算,提升科研效率与可重复性。

j2k3l4的博客 670

flink sql - 问题系列与解决系列

flink sql 调试-注意点1、布尔类型的坑2、cdc 表[kafka/pg等],要写对表主键,特别是flink sql 有group by 的情况 1、布尔类型的坑 数据库 deleted = 'false' , flink sql 要替换为 deleted is false 2、cdc 表[kafka/pg等],要写对表主键,特别是flink sql 有group by 的情况 现象:a 表主键是三个字段的联合主键[PRIMARY KEY (id,parent_id,entity_farm_

a123147abc的博客 7852

Cartographer实现双雷达建图

TRAJECTORY_BUILDER_2D.num_accumulated_range_data = 2 --执行一次前端匹配需要的激光扫描分段数量。关于num_subdivisions_per_laser_scan。num_laser_scans =2,--订阅激光扫描话题数量。Frame改为 前雷达laser_1 后雷达laser_2。雷达的话题由scan改为,前scan_1 后scan_2。有些人说需要改成2,但我这里改成2反而不行,1才可以。

Studying…… 1261

YOLOv11的嗅觉传感器数据关联-(特殊场景下的气味-视觉联合分析)

摘要 本文探讨了YOLOv11与嗅觉传感器融合的多模态感知技术。通过分析YOLOv11的架构特性与嗅觉传感器的数据特点,提出了三种融合策略:数据级、特征级和决策级融合。系统采用硬件同步方案实现时空对齐,包含视觉采集模块、嗅觉传感模块和边缘计算单元。环境配置基于Python 3.9和PyTorch 2.0,支持实时多模态数据处理。该技术可应用于环境监测、工业检测等场景,突破单一视觉感知的局限性。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 629

Flink SQL --JDBC connector

一、Table API & SQL 二、SQL Client 2.1、配置 1、添加依赖jar flink-connector-jdbc_2.11-1.12.3.jar mysql-connector-java-5.1.48.jar 2、重启flink stop-cluster.sh start-cluster.sh 注意: 如果不重启的话,无法加载到上面的依赖jar java.lang.ClassNotFoundException: org.apache.flink.connector.j

chbxw 5495

【花雕学编程】Arduino ESP32 SPP 之使用Servo库控制360度舵机正反转

使用 Servo 库控制 360 度舵机的正反转是 Arduino ESP32 SPP 应用中的一个重要案例,具有简易控制接口、灵活速度调节和高兼容性等特点,广泛应用于机器人、自动化装置和动态艺术等领域。在第三个案例中,使用map()函数将用户输入的速度参数(范围0-100)映射到舵机控制的角度(0-180),使得用户能够以更人性化的方式控制舵机速度。5、创新:Arduino可以让你用电子的方式来表达你的创意和想象,你可以用Arduino来制作各种有趣和有用的项目,如机器人、智能家居、艺术装置等。

雕爷学编程 1469

Flink SQL 获取FileSystem时,如果FileName发生更改在则会报错

FLink SQL在设定各类数据源和数据目标端的时候非常方便,可以说写个建表语句就能获取各种各样的数据。但是在使用FileSystem获取某目录下的数据时需要小心使用,比如: CREATE TABLE `cfg_city`( `provincecode` int, `city_id` int, `city_name` string ) WITH ( 'connector'='filesystem', 'path'='hdfs://pathroot/cfg_city', .

青冬的博客 1323

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

flink sql状态清理问题

备注:本文摘抄与Flink社区邮件列表,是个典型问题,特此记录,Q为提问者,A为解答 Q: 在使用flink sql的过程中遇到如下情况,在配置了如下选项后: val config = tableConfig.getConfiguration() config.setString(“table.exec.mini-batch.enabled”, “true”) config.setString(“table.exec.mini-batch.allow-latency”, “5s”) config.setSt

ChengYanan的博客 3325

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

flink on yarn常见问题及解决方法汇总

持续更新flink on yarn在实际使用中的问题和解决方式,与君共勉

fengjian的博客 5562

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

flink 有状态udf 引起血案一

场景最近在做一个画像的任务,sql实现的,其中有一个udf,会做很多事情,包括将从redis读出历史值加权,并将中间结果和加权后的结果更新到redis。大家都知道,fli...

大数据星球-浪尖 9050

Table Api & SQL查询配置(状态清理)

该文章主要是对Flink官网相关内容进行翻译,原文地址: https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/table/streaming/query_configuration.html 无论Table API和SQL查询的输入是有界批处理输入还是无界流输入,其语义都相同。在许多情况下,对流输入的连续查询能够计算...

数据工匠记 5426

flink-流上的join (1.12.2)

flink-流上的join (1.12.2) 一、简介 本篇主要围绕flink table & sql进行实践。 其中文章中大部分内容官方文档有详细记载。 由于环境和代码编写问题,官方示例并不能完全用于代码实践,所以此篇文章通过kafka+mysql进行简单实践。 环境: kafka 2.4 flink 1.12.2 mysql57 java8 二、时间属性 首先对于实时join来说,第一个要讨论的就是时间属性。 在flink sql中,给出了两种时间属性,一个是系统处理时间,一个是数据本身的自

铛铛当的博客 1005
上一篇: 一石激起千层浪:FlinkSQL Join维表和其他疑问的梳理
下一篇: Flink吐血总结,学习与面试收藏这一篇就够了!!!
大数据左右手
博客等级 码龄9年 475粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值