Flink动态ClickhouseSink+自动建表

阿里SLS -> Flink -> ClickHouseSink 写入动态列 1.ClickHouseUtil 1.需求 需求:通过flink解析SLS中的JSON数据并以动态列的形式存入clickhouse中,方便事件的在线检索,开发一个Flink的clickhouseSink。其中实现动态列最关键的就是根据解析出来的字段完成对sql的拼接。 2.代码 1.ClickHouseUtil import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; pub 阅读详情

通过自定义注解的形式,对JdbcSink进行封装,支持自动建表、自动拼接insert语句

主类

package flink.security.sink;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import lombok.Cleanup;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcExecutionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.streaming.api.functions.sink.SinkFunction;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 通用clickhouse sink
 */
@Slf4j
public class SecurityClickHouseSink {
    public static <T> SinkFunction<T> getSink(Class<T> model) throws Exception {
        createTable(model);
        String sql = createSql(model);
        return JdbcSink.sink(
                sql,
                new JdbcStatementBuilder<T>() {
                    @SneakyThrows
                    @Override
                    public void accept(PreparedStatement preparedStatement, T t) throws SQLException {
                        Class<?> obj = t.getClass();
                        Field[] declaredFields = obj.getDeclaredFields();
                        Stream<Field> usedFields = getUsedFields(declaredFields);
                        List<Field> fields = usedFields.collect(Collectors.toList());
                        for (int i = 0; i < fields.size(); i++) {
                            fields.get(i).setAccessible(true);
                            Object o = fields.get(i).get(t);
                            if (o instanceof Boolean) {
                                preparedStatement.setInt(i + 1, (Boolean) o ? 1 : 0);
                            } else if (o instanceof List) {
                                preparedStatement.setString(i + 1, JSONUtil.toJsonStr(o));
                            } else if (o instanceof Date) {
                                preparedStatement.setString(i + 1, DateFormatUtil.toYmdHms(((Date) o).getTime()));
                            } else {
                                preparedStatement.setObject(i + 1, o);
                            }
                        }
                    }
                },
                new JdbcExecutionOptions.Builder()
                        .withBatchSize(10000)
                        .withBatchIntervalMs(1000L * 10)
                        .build(),
                new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
                        .withDriverName(Constant.CLICKHOUSE_DRIVER_NAME)
                        .withUrl(Constant.CLICKHOUSE_URL)
                        .build()
        );
    }

    /**
     * 组装sql
     */
    private static <T> String createSql(Class<T> model) {
        // 1、获取表名
        SecurityTable tableAnno = model.getAnnotation(SecurityTable.class);
        if (tableAnno == null) {
            throw new RuntimeException("缺少SecurityTable注解");
        }
        String tableName = tableAnno.value().trim();
        // 2、获取字段名
        Field[] declaredFields = model.getDeclaredFields();
        // 2.1 过滤掉带SecurityField注解且isTableFile=false的字段
        Stream<Field> fields = getUsedFields(declaredFields);
        // 3.2 获取字段
        List<String> fieldnames = fields.map(SecurityClickHouseSink::getTableFiledName).collect(Collectors.toList());
        // 3、获取占位符
        List<String> placeholder = fieldnames.stream().map(f -> "?").collect(Collectors.toList());

        if (ObjectUtil.isEmpty(placeholder) || ObjectUtil.isEmpty(fieldnames)) {
            throw new RuntimeException("缺少必要的表字段!");
        }
        // 分布式表需要插入视图
        String distributed = tableAnno.distributed();
        return "insert into " + tableName + (StringUtils.isBlank(distributed) ? "" : "_mr") + "(" + StringUtils.join(fieldnames, ",") + ") values(" + StringUtils.join(placeholder, ",") + ")";
    }

    /**
     * 组装sql
     */
    private static <T> void createTable(Class<T> model) throws Exception {
        // 1、获取表名
        SecurityTable tableAnno = model.getAnnotation(SecurityTable.class);
        if (tableAnno == null) {
            throw new RuntimeException("缺少SecurityTable注解");
        }
        String tableName = tableAnno.value().trim();
        String engine = tableAnno.engine().trim();
        String distributed = tableAnno.distributed().trim();
        String cluster = tableAnno.cluster().trim();
        // 2、获取字段名
        Field[] declaredFields = model.getDeclaredFields();
        // 2.1 过滤掉带SecurityField注解且isTableFile=false的字段
        Stream<Field> fields = getUsedFields(declaredFields);
        // 3.2 获取字段
        List<String> fieldnames = fields.map(f -> {
            String wordType = f.getType().getName();
            SecurityField annotation = f.getAnnotation(SecurityField.class);
            switch (wordType) {
                case "java.lang.String":
                    wordType = "String";
                    break;
                case "java.util.Date":
                case "java.time.LocalDate":
                case "java.time.LocalDateTime":
                    wordType = "Datetime";
                    break;
                case "java.util.List":
                    wordType = "String";
                    break;
                case "boolean":
                    wordType = "UInt8";
                    break;
                case "java.lang.Double":
                    wordType = "Decimal(15,3)";
                    break;
                case "java.lang.Integer":
                    wordType = "Int32";
                    break;
                case "java.lang.Long":
                    wordType = "Int64";
                    break;
                default:
                    wordType = "String";
            }
            if (annotation == null) {
                return f.getName() + " " + wordType + " comment ''";
            } else {
                String comment = Optional.of(annotation.comment()).orElse("").replace("'", "`").replace("'", "`");
                return (StringUtils.isBlank(annotation.value()) ? f.getName() : annotation.value()) + " " + wordType + " comment '" + comment + "'";
            }
        }).collect(Collectors.toList());

        if (ObjectUtil.isEmpty(fieldnames)) {
            throw new RuntimeException("缺少必要的表字段!");
        }

        String cols = StringUtils.join(fieldnames, ",\n");

        try(Connection connection = DriverManager.getConnection(Constant.CLICKHOUSE_URL, Constant.CLICKHOUSE_USERNAME, Constant.CLICKHOUSE_PASSWORD);) {
            String localtable = String.format("create table if not exists  %s %s (%s) engine = %s", tableName, cluster, cols, engine);
            log.error(localtable);
            try (PreparedStatement preparedStatement = connection.prepareStatement(localtable)) {
                preparedStatement.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("建表失败请查看建表语句:\r\n" + localtable);
            }
            if (StringUtils.isNotBlank(distributed)) {
//                localtable = "CREATE TABLE default.PowerConsumption ON CLUSTER default_cluster AS default.PowerConsumption_local\n" +
//                        "ENGINE = Distributed(default_cluster, default, PowerConsumption_local, rand());";
                localtable = String.format("create table if not exists  %s_mr  %s AS %s engine = %s", tableName, cluster, tableName, distributed);
                log.error(localtable);
                try (PreparedStatement preparedStatement = connection.prepareStatement(localtable)) {
                    preparedStatement.executeUpdate();
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("建表失败请查看建表语句:\r\n" + localtable);
                }
            }
        }
    }

    private static String getTableFiledName(Field field) {
        SecurityField annotation = field.getAnnotation(SecurityField.class);
        if (annotation == null) {
            return field.getName();
        } else {
            String value = annotation.value();
            if (StringUtils.isBlank(value)) {
                return field.getName();
            }
            return value;
        }
    }

    private static Stream<Field> getUsedFields(Field[] declaredFields) {
        return Arrays.stream(declaredFields).filter(field -> {
            SecurityField annotation = field.getAnnotation(SecurityField.class);
            return annotation == null || annotation.isTableField();
        });
    }

}

 自定义注解

package cn.chinaunicom.sdsi.flink.security.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityField {
    String value() default "";
    boolean isTableField() default true;
    String comment() default "''";
}
package cn.chinaunicom.sdsi.flink.security.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityTable {
    String value();
    String engine() default "";
    String distributed() default "";
    String cluster() default "";
}

使用方式

SingleOutputStreamOperator<SecurityAlarms> process = attack.connect(broadcastStream).process(new BroadcastProcessFunction<SecurityAlarms, Co...

SinkFunction<SecurityAlarms> sink = SecurityClickHouseSink.getSink(SecurityAlarms.class);
process.addSink(sink);

Bean的定义

@Data
@SecurityTable(value = "security_data_center.security_alarms",
        cluster = "on cluster cluster_p2_r2_2345",
        engine = "ReplicatedReplacingMergeTree partition by (toYYYYMMDD(collectorReceiptTime)) order by (collectorReceiptTime,eventId)",
        distributed = "Distributed(cluster_p2_r2_2345,security_data_center,security_alarms,rand())")
public class SecurityAlarms extends CommonFileds{

    @SecurityField(comment = "数据接收来源【数据接收来源(安恒云)】")
    private String dispatchSource;
    @SecurityField(comment = "活动目录唯一标识【活动目录唯一标识】")
    private String DN;
    @SecurityField(comment = "DPI告警类型【DPI数据告警类型】")
    private String DPIAlertType;
}

多维聚合实战:从SQL GROUP BY到OLAP立方体的工程化落地 多维聚合是现代数据分析的核心能力,本质是基于维度建模对高基数业务数据进行可复用、可钻取的分组聚合操作。其原理源于OLAP立方体思想,通过维度(时间/地域/产品等层级结构)、度量(可加性指标)与事实表协同实现动态切片与上卷。技术价值在于打破传统SQL中GROUP BY的静态降维局限,支撑BI自助分析、实时监控与AI特征生成等场景。本文聚焦工程实践,覆盖SQL的GROUPING SETS/CUBE语法、Pandas pivot_table优化与Polars LazyFrame查询下推,并结合电商大促案例详解维度 阅读详情

相关推荐

flink实战 -- 数据写入clickhouse(ClickHouseSink)

简介 本文主要介绍如何通过Flink JDBC Connector将数据写入ClickHouse以及直接使用Flink JDBC Connector操作ClickHouse存在什么样的问题。 Flink JDBC Connector的使用 通过JDBCUpsertTableSink.Builder创建一个JDBCUpsertTableSink对象,JDBCUpsertTableSink需要的参数...

Vision 1万+

Flink-clickhousesink

1需求 需求:准备将一些明细数据存入clickhouse中,方便事件的在线检索。开发一个Flink的clickhouseSink; flink版本 1.10 ,flink版本1.12 两个版本 ClickHouse的java api也是基于jdbc的接口来做的。所以按照 JDBCSinkFunction 继承RichSinkFunction来实现。并且还需要创建一个JDBCOutputFormat来用于真正的输出数据到clickhouse 2.代码 JdbcSinkFunction 代码

旺旺的博客 3760

【Flink】flink ClickHouseSink--数据写入ClickHouse

1.概述 转载:Flink自定义ClickHouseSink–数据写入ClickHouse 这个版本是flink1.11版本才有的相关功能。 遇到需要将Kafka数据写入ClickHouse的场景,本文将介绍如何使用Flink JDBC Connector将数据写入ClickHouse Flink JDBC Connector Flink JDBC源码: /** * Default JDBC dialects. */ public final class JdbcDialects { private

九师兄 4511

Flink入门实战之Sink clickhouse

clickhouse 建表语句 // 建数据库 CREATE database test_db; // 建MergeTree表 CREATE table test_db.test( id Int32, date Date, age Int8 ) engine=MergeTree() order by id ; // 测试插入 INSERT into test_db.test values (4, '2020-01-12', 19), (5, '2020-02-13', 3); // 查看 SELEC.

xiaoc100200的博客 2789

Flink ClickHouseSink:高效写入ClickHouse的大数据

在大数据场景中,将Flink与ClickHouse结合使用可以实现高效的数据处理和存储。本文将介绍如何使用Flink的ClickHouseSink将数据有效地写入ClickHouse,并提供相应的源代码示例。本文介绍了如何使用Flink的ClickHouseSink将大数据有效地写入ClickHouse数据库。这样,我们就完成了使用Flink的ClickHouseSink将数据写入ClickHouse的配置和代码。首先,确保已经安装了Flink和ClickHouse,并且已经配置好了它们的环境。

TechWhiz的博客 832

【信息科学与工程学】计算机科学与自动化——第三百零四篇 高性能系统开发指南01

高并发:限流、熔断、降级高性能:红包拆分、多级缓存、大Key/热Key拆分、Pipeline、线程池、RocketMQ零丢失、幂等、分布式锁、集群缩容、分批入库海量数据处理:日志分表分片、跨表查询、冷热分层、大数据引擎服务器选型系统安全:网关安全、服务器安全、云存储安全、风控、监控、测试数据一致性:缓存与数据库一致性、大厂方案、蓝绿回滚、流量一致性、流程一致性项目与团队管理:流程问题、管理问题(风险、成本、人员、文档、培训)稳定性建设:上线三板斧、线上五步闭环、混沌工程、容量规划。

weixin_49199313的博客 251

【Flink反压排查】别再一上来就加机器了

生产环境遇反压,切勿盲目扩容。应按“先治下游、再控上游、最后扩容”顺序处理:先通过Flink Web UI定位瓶颈,排查数据倾斜、代码性能、GC及外部系统问题;优先优化下游算子(如两阶段聚合、对象复用);其次调优Kafka消费限速;再调整网络与Checkpoint参数;仅当所有优化无效时才考虑扩容。此流程可有效根治反压,避免雪崩。

zy123456nn的博客 420

flink实战--数据写入clickhouse(自定义ClickHouseSink 与jdbc sink)

简介 Clickhouse 支持http协议的web方式进行访问,也支持JDBC或者ODBC的驱动程序的客户端进行访问,我们使用Flink操作Clickhouse ,可以想操作mysql一样通过JDBC的方式进行访问,本文将具体介绍flink实时写入Clickhouse的实现方式,对于flink自定义sink,参考上篇博客:https://blog.youkuaiyun.com/aA518189/article/details/86544844。 Flink数据写入Clickhouse步骤 ...

阿华田的博客 8170

个性化学习平台架构设计与性能优化实践

微服务架构在现代教育技术中扮演着关键角色,其核心原理是通过解耦服务实现弹性扩展。基于Spring Cloud Alibaba的微服务生态,结合TensorFlow等机器学习框架,可以构建智能化的个性化学习平台。这类系统通过实时采集学习行为数据,运用动态权重分配算法和IRT理论模型,实现精准的题目推荐和学情分析。在工程实践中,采用Flink实时计算和ClickHouse存储方案能有效处理海量数据,而ONNX Runtime等推理优化技术可确保智能体服务的低延迟响应。教育行业特别关注数据安全,需实施字段级加密和

weixin_33742618的博客 396

flink-clickhouse-sink:Flink水槽的Clickhouse

Flink-ClickHouse-Sink 描述 用于数据库的器。 由。 用于将数据加载到ClickHouse的高性能库。 它有两个触发器来加载数据:超时和缓冲区大小。 版本图 闪烁 flink-clickhouse-sink 1.3。* 1.0.0 1.9。* 1.3.1 安装 Maven中央 < dependency> < groupId>ru.ivi.opensource</ groupId> < artifactId>flink-clickhouse-sink</ artifactId> < version>1.3.1</ version> </ dependency> 用法 特性 flink-clickhouse-sink使用配置属性的两个部分:common和操作员链中的每个接收器。 共同的部分(像全局一样使用): clickhouse

ArcGIS实战:手把手教你用DEM和灾害点数据,5步搞定地质灾害风险区划图

本文详细介绍了使用ArcGIS进行地质灾害风险评估的完整流程,从DEM和灾害点数据出发,通过五步标准化操作生成专业风险区划图。重点讲解了信息量模型的应用,帮助读者掌握GIS技术在地质灾害易发性评价中的实践方法,提升风险评估效率与准确性。

weixin_30696427的博客 426

Flink_Clickhouse_Sink

Flink Sink Flink-clickhouse JDBC sink: 1.若是没有接触过clickhouse的同学可以先看下java连接clickhouse的方法。 @Test public void clickhouseTest(){ //使用clickhouse官网推荐的JDBC连接方式,使用了官网推荐的连接驱动 String url = "jdbc:clickhouse://xxxxxx:8123/secisland"; ClickHouseProperties p

分享大数据、java相关的技术 2249

5分钟看懂LTE注网全流程:从PLMN选择到小区驻留的log分析技巧

本文深入解析了LTE终端从开机到成功驻留小区的完整流程,重点分享了基于QXDM和QCAT工具的log分析实战技巧。文章详细拆解了PLMN选择、System/Band扫频、小区搜索与同步、系统消息解析及驻留等关键步骤,并提供了高频问题场景的排查思路,帮助网络优化工程师快速定位终端侧、射频环境或网络配置问题。

app77的博客 434

flink读clickhouse,自定义sink和source

flink分批读clickhouse数据,处理后再存clickhouse。存量数据:按天分批取数据处理;增量数据:每隔一段时间拉取一次数据。

jianm_liu的博客 1956

Workbench中直接调用ICEM CFD进行网格划分

自从ANSYS 12.0之后,ICEM CFD就从Workbench中被分离出去,作为一个独立的程序使用了。取而代之的是Meshing模块。 在Meshing的属性节点菜单中右键点击Mesh,选择Insert > Method,插入方法。 选择需要划分网格的几何体,点击apply。此时Geometry显示为1 Body。 设置Method为MultiZone,如果不设置成这个的话,找不到进入ICEM CFD的入口。如果要划分四面体,可以在Mapped Mesh Type中选择其他类型。

Yan的博客 6513

你的App合规吗?手把手教你用Camille生成隐私调用报告给老板看

本文详细介绍了如何使用开源工具Camille进行Android App隐私合规检测,并生成高管易懂的隐私调用报告。通过动态插桩技术和多维度分析,Camille能有效识别高风险API调用,并将其映射到具体法律条款,帮助技术团队与法务、产品等非技术部门高效沟通,确保App符合《个保法》等法规要求。

weixin_33726318的博客 425

【自定义Source、Sink】Flink自定义Source、Sink对ClickHouse进行读和批量写操作_flink 读取clickhouse

使用 Flink-jdbc-connector + 批量写入 + sql语句的预编译 写入 Clickhouse。* @param object: clickhouse表PO对象的属性字段所对应的数据类型。* 1. getDeclaredFields():获取所有的字段,不会获取父类的字段。* 2. getFields(): 只能会public字段,获取包含父类的字段。* @param fields: clickhouse表PO对象的属性字段。* 对预编译之后的sql语句进行占位符替换。

2401_84186109的博客 1231

微信支付V2,jsapi支付拉起收银台报错:调用支付JSAPI缺少参数:total_fee

拉起收银台报错

m0_54355172的博客 3873

【亲测免费】 Flink ClickHouse Sink:用于Apache Flink的高性能ClickHouse数据库连接器

Flink ClickHouse Sink:用于Apache Flink的高性能ClickHouse数据库连接器 简介 Apache Flink是一个用于分布式数据处理的开源框架。ClickHouse是一个快速、列式数据库,特别适合在大规模数据集上执行高效的数据分析。Flink ClickHouse Sink是一个将Flink与ClickHouse连接的工具,它提供了一种高性能、可靠的方法,将Fl...

gitblog_00067的博客 1383

基于大模型的自动驾驶智能体:Agent-Driver架构、训练与实战指南

自动驾驶系统正从传统的模块化流水线向以智能体为核心的新范式演进。其核心原理在于将车辆视为一个能够与环境持续交互的自主决策智能体,通过多模态感知、记忆管理和基于大模型的推理能力,实现对复杂交通场景的理解与应对。这一技术价值在于利用大模型的常识推理和泛化能力,有望解决传统方法在长尾场景下面临的挑战。在应用场景上,智能体驱动架构特别适合处理动态交互密集、需高层语义理解的城区驾驶、无保护转弯及突发状况应对。本文聚焦的Agent-Driver项目,正是这一方向的典型实践,它通过融合视觉、语言大模型与强化学习,构建了一

weixin_28729843的博客 529

棋牌App逆向工程实战:透视风险分析与反作弊加固思路

在移动应用安全领域,逆向工程是一项关键的攻防技术,它通过静态分析、动态调试与协议抓包等手段,深入还原客户端真实逻辑。这项技术的核心原理,是从已编译的安装包中提取数据流,验证服务端下发的信息是否超出了客户端应有的可见范围。从技术价值看,安全测试人员可以借此定位协议层、内存层与缓存层的潜在风险,为反作弊体系提供精准的加固建议。在棋牌类App等实时对局场景中,逆向工程常被用于排查牌局信息泄露类隐患,而合规授权与测试环境是开展此类研究的必要前提。本文结合一次棋牌App的安全评估实践,梳理了从环境准备到数据流还原的完

weixin_29326909的博客 176

flink 1.9 sink clickhouse

由于flink 1.9 版本的jdbc 有不少ck 类型并不支持,例如Map 类型。故重写了一个clickhouse sink。 基本依赖: <!-- https://mvnrepository.com/artifact/ru.yandex.clickhouse/clickhouse-jdbc --> <dependency> <groupId>ru.yandex.clickhouse</groupId>

qq_38363255的博客 829

【XSRP软件无线电】基于软件无线电平台的FM数字接收系统设计(含有完整工程资料获取链接)

随着无线通信技术的快速发展,软件无线电(Software Defined Radio, SDR)作为一种新兴的无线通信技术,逐渐成为研究和应用的热点。传统的无线接收机通常依赖于专用硬件实现其功能,灵活性和可扩展性有限。相较之下,软件无线电利用软件来实现信号处理和调制解调功能,能够适应多种频率和调制方式,使无线通信系统更加灵活和高效。在FM(频率调制)广播中,数字接收技术的应用不仅提高了信号的抗干扰能力,还能提供更好的音质和更多的附加信息(如广播电台名称、节目内容等)。

嵌入式技术开发 1368

【亲测免费】 探索高效数据流处理:Flink-ClickHouse-Sink深度解析

探索高效数据流处理:Flink-ClickHouse-Sink深度解析 在大数据处理的浩瀚世界中,连接高效的数据流处理系统与强大的数据库成为了一项关键的技术挑战。今天,我们聚焦于一个名为 Flink-ClickHouse-Sink 的开源项目,这是一款专为Apache Flink设计的ClickHouse数据接收器,旨在以高速度和高效率将数据流入到ClickHouse中。接下来,我们将从项目介绍、...

gitblog_00096的博客 976

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

flume自定义sink开发——flume clickhouse sink

flume优点之一就是支持插件扩展功能,现在clickhouse流行,数据想直接写入clickhouse,flume官网看不了一样,没有clickhouse sink,没有咱们就自已写呗。 网上开发自定义拦截器的文章很多,开发sink的反而找不到,特记录一下,供大家参考。 一、开发流程 搭建flume开发环境。 新建一个类,实现Configurable接口,继承AbstractSink类。 重写configure、start、stop、process方法。 编译打jar包,放到flume的lib目录下。

俊伟的博客 3080

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【自定义Source、Sink】Flink自定义Source、Sink对ClickHouse进行读和批量写操作

Flink自定义Source、Sink读取批量写入ClickHouse

打怪升级 1464

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: doris docker部署和本地化部署 1.2.4.1版本
下一篇: 常用正则表达式
JNWsong
博客等级 码龄9年 287粉丝 204原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值