news 2026/10/17 9:27:10

Flink+Phoenix+HBase实时链路实战:架构设计与性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flink+Phoenix+HBase实时链路实战:架构设计与性能优化

1. 为什么要在实时链路里把 Flink、Phoenix 和 HBase 串起来

第一次接触 Flink-Phoenix-HBase 这套组合,是在一个实时风控场景里。业务方要求:上游埋点数据进来后,5 秒内要能按用户维度查到最近行为明细,同时还要支持多维度的即席聚合查询。当时团队第一反应是用 Flink 直接写 HBase,因为 HBase 的随机读写能力确实强,单行百万级 QPS 不是问题。但真上手才发现,HBase 原生 API 只能按 RowKey 查,业务方要的是“按用户 ID 查、按时间范围过滤、按事件类型筛选”这种组合条件,纯 HBase 客户端写起来极其痛苦,每个查询条件都要自己拼 Filter,代码又长又容易出错。

后来引入了 Phoenix。Phoenix 本质上是构建在 HBase 之上的 SQL 层,它把 HBase 的 RowKey 设计、列族映射、二级索引这些概念包装成了标准 SQL。你可以像用 MySQL 一样写SELECT * FROM user_behavior WHERE user_id = 'xxx' AND event_time > '...',Phoenix 会自动帮你翻译成 HBase 的 Scan 和 Filter。更关键的是,Phoenix 支持二级索引,这对多维度查询来说是刚需。而 Flink 负责的是流式写入和实时计算,它把上游 Kafka 的数据做清洗、聚合、打宽之后,通过 Phoenix 的 JDBC 连接批量写入 HBase。整条链路跑通之后,写入延迟稳定在 200ms 以内,查询响应基本在 50ms 左右,业务方那边算是满意了。

这套组合适合谁呢?如果你正在做实时数仓、用户画像、实时风控、IoT 设备状态监控这类场景,需要“流式写入 + 低延迟点查 + 多维度即席查询”,那 Flink-Phoenix-HBase 值得认真考虑。它不适合的是:纯离线批量分析(那用 Hive/Spark 更合适)、对事务要求极高的场景(HBase 只支持单行事务)、以及数据量很小、用 MySQL 就能搞定的情况。下面我会从架构设计、核心细节、实操步骤、踩坑记录几个方面,把这套组合拆开讲清楚。

2. 整体架构设计与方案选型背后的逻辑

2.1 三层架构的职责划分

这套组合的架构其实很清晰,分三层:计算层、SQL 层、存储层。Flink 在计算层,负责流式数据的接入、转换和写出;Phoenix 在 SQL 层,负责把 SQL 翻译成 HBase 的读写操作,同时管理二级索引;HBase 在存储层,负责数据的持久化和随机读写。

为什么不让 Flink 直接写 HBase?因为 Flink 的 HBase Connector 虽然能用,但它只支持按 RowKey 写入,不支持二级索引。业务方要查“某个用户最近 7 天的所有行为”,如果 RowKey 设计成user_id + timestamp,那按用户查没问题,但要是想查“某个事件类型下所有用户的行为”,就得全表扫描,性能直接崩掉。Phoenix 的二级索引就是解决这个问题的:你可以在event_type上建索引,Phoenix 会自动维护一张索引表,查询时先走索引表定位 RowKey,再回主表取数据。

为什么不让 Phoenix 直接对接 Kafka?Phoenix 本身是 JDBC 驱动,它没有流式消费能力。Flink 的优势在于它有 Exactly-Once 语义、有状态管理、有窗口计算,这些是 Phoenix 不具备的。所以 Flink 负责“流”,Phoenix 负责“SQL 映射”,HBase 负责“存”,三者各司其职。

2.2 RowKey 设计:整个方案的地基

RowKey 设计是 HBase 最核心的问题,没有之一。RowKey 决定了数据在 Region 上怎么分布,也决定了查询能不能走索引。在这套组合里,RowKey 设计要同时考虑 Flink 写入的吞吐和 Phoenix 查询的效率。

常见的 RowKey 设计模式有几种:盐值 + 业务主键 + 时间戳、反转时间戳 + 业务主键、哈希 + 业务主键。我以用户行为表为例,讲一下我的选择过程。

假设表结构是:用户 ID、事件类型、事件时间、事件详情。查询需求有两个:一是按用户 ID 查最近 N 条行为,二是按事件类型 + 时间范围查所有用户的行为。

如果 RowKey 设计成user_id + event_time,那第一个查询很快,第二个查询就得全表扫描。如果设计成event_type + event_time + user_id,那第二个查询快,第一个查询又慢了。这时候就需要二级索引。我的做法是:主表 RowKey 用salt + user_id + event_time,其中 salt 是 0-15 的随机数,用来打散数据,避免热点。然后在 Phoenix 里对event_type和event_time建联合索引。这样第一个查询走主表,第二个查询走索引表。

盐值的计算方式很简单:salt = abs(user_id.hashCode()) % 16。为什么选 16?因为 Region 数量通常是 2 的幂次,16 个盐值可以均匀分布到 16 个 Region 上。如果 Region 数量少,比如只有 4 个,那盐值取 4 就行。盐值太多会导致 Scan 时跨 Region 查询变多,反而降低性能。

注意:盐值一旦确定,就不能随便改。因为 RowKey 变了,历史数据就查不到了。如果非要改,得做数据迁移,成本很高。

2.3 Phoenix 二级索引的选型:全局索引 vs 本地索引

Phoenix 支持两种二级索引:全局索引和本地索引。全局索引适合读多写少的场景,因为索引表是独立的 HBase 表,写数据时要同步更新索引表,写放大比较明显。本地索引适合写多读少的场景,索引数据和主表数据在同一个 Region,写入时不需要跨 Region 同步,但查询时可能要扫描多个 Region。

在我们的场景里,写入 QPS 大概在 5 万左右,查询 QPS 在 2000 左右,读少写多。所以我选了本地索引。本地索引的创建语法是:

CREATE LOCAL INDEX idx_event_type_time ON user_behavior (event_type, event_time);

本地索引有一个坑:它会把索引数据存到主表的一个隐藏列族里,这个列族的名字是_IDX_开头的。如果你用 HBase Shell 去看表结构,会看到多了一个列族,别慌,那是正常的。

全局索引的创建语法是:

CREATE INDEX idx_event_type_time ON user_behavior (event_type, event_time);

全局索引适合查询条件里包含索引前缀的场景,比如WHERE event_type = 'click' AND event_time > '...'。如果查询条件里没有索引前缀,全局索引会失效,变成全表扫描。本地索引则没有这个限制,但查询时可能要扫多个 Region。

2.4 Flink 写入方式:JDBC 批量写入 vs HBase API 写入

Flink 写 Phoenix 有两种方式:一种是走 Phoenix 的 JDBC 驱动,用PreparedStatement批量插入;另一种是走 HBase 的BufferedMutatorAPI,直接写 HBase。两种方式各有优劣。

JDBC 方式的优点是简单,SQL 写起来直观,而且 Phoenix 会自动维护索引。缺点是性能不如 HBase API,因为 JDBC 有网络开销和 SQL 解析开销。HBase API 方式的优点是快,直接写 HBase,没有 SQL 层开销。缺点是要自己维护索引,如果表有二级索引,你得手动更新索引表,非常麻烦。

我选的是 JDBC 方式,因为索引维护太重要了。Phoenix 的 JDBC 驱动支持批量提交,设置phoenix.mutate.batchSize参数可以控制每批提交多少条。我一般设成 1000,实测下来吞吐和延迟比较平衡。如果设成 5000,吞吐会高一些,但延迟会变大,因为要等一批攒够才提交。

String url = "jdbc:phoenix:zk1,zk2,zk3:2181:/hbase"; Properties props = new Properties(); props.setProperty("phoenix.mutate.batchSize", "1000"); Connection conn = DriverManager.getConnection(url, props);

提示:JDBC URL 里的 ZK 地址要写全,不然连不上。如果 ZK 有 Chroot,比如/hbase,也要加上。

3. 核心细节解析与实操要点

3.1 Phoenix 表结构设计:列族和列限定符的映射

Phoenix 把 HBase 的列族和列限定符映射成了 SQL 的表和列。建表的时候,你可以指定列族,也可以不指定,Phoenix 会用默认列族0。我一般会显式指定列族,因为不同列族的读写特性不一样。比如,经常一起查询的列放在同一个列族,不常查的列放在另一个列族。

CREATE TABLE user_behavior ( user_id VARCHAR NOT NULL, event_time BIGINT NOT NULL, event_type VARCHAR, event_detail VARCHAR, CONSTRAINT pk PRIMARY KEY (user_id, event_time) ) SALT_BUCKETS = 16;

这里SALT_BUCKETS = 16就是前面说的盐值,Phoenix 会自动帮你加盐,不需要自己算。主键是user_id + event_time,Phoenix 会把它们拼成 RowKey。event_detail这种大字段,如果很长,可以考虑单独放一个列族,避免影响主键列的读取性能。

Phoenix 的数据类型和 HBase 的字节数组之间有一套映射规则。VARCHAR映射成 UTF-8 字节数组,BIGINT映射成 8 字节长整型,INTEGER映射成 4 字节整型。如果你用 HBase API 直接写数据,要注意字节序,Phoenix 用的是大端序。如果字节序不对,Phoenix 查出来的数据会是乱码。

3.2 Flink 端的数据转换与攒批策略

Flink 从 Kafka 读到的数据通常是 JSON 格式,需要先解析成 POJO,再转换成 Phoenix 的插入语句。解析的时候要注意字段类型匹配,比如event_time在 JSON 里可能是字符串,要转成Long。如果类型不匹配,Phoenix 写入时会报错。

攒批策略是 Flink 写入性能的关键。Flink 的JdbcSink支持batchSize和batchIntervalMs两个参数。batchSize是每批多少条,batchIntervalMs是每批最长等多久。我一般设成batchSize = 1000,batchIntervalMs = 200。这样即使数据量小,最多等 200ms 也会提交,不会让数据一直攒着。

JdbcExecutionOptions execOptions = JdbcExecutionOptions.builder() .withBatchSize(1000) .withBatchIntervalMs(200) .withMaxRetries(3) .build();

withMaxRetries(3)是重试次数,网络抖动或者 Phoenix 短暂不可用时,会自动重试。重试间隔是指数退避,第一次 100ms,第二次 200ms,第三次 400ms。

注意:如果 Phoenix 挂了超过重试次数,Flink 任务会失败。这时候要看 Flink 的 Checkpoint 配置,如果开启了 Checkpoint,任务重启后会从上次 Checkpoint 恢复,不会丢数据。

3.3 二级索引的维护与查询优化

Phoenix 的二级索引是自动维护的,你不需要手动更新索引表。但是,索引表会占用额外的存储空间,而且写入时会增加延迟。如果索引太多,写入性能会明显下降。我一般只建必要的索引,比如查询频率最高的那两三个字段。

查询的时候,Phoenix 会自动选择走主表还是走索引表。你可以用EXPLAIN命令查看执行计划:

EXPLAIN SELECT * FROM user_behavior WHERE event_type = 'click' AND event_time > 1234567890;

如果执行计划里出现RANGE SCAN OVER INDEX,说明走了索引。如果出现FULL SCAN,说明没走索引,可能是索引没建对,或者查询条件不匹配索引前缀。

索引失效的常见原因有几个:一是查询条件里用了函数,比如WHERE UPPER(event_type) = 'CLICK',这样索引就用不上;二是查询条件里的字段类型和索引字段类型不一致,比如索引是VARCHAR,查询时传了INTEGER;三是查询条件里用了OR,Phoenix 对OR的支持不太好,可能会退化成全表扫描。

3.4 连接池配置与资源管理

Phoenix 的 JDBC 连接创建开销比较大,因为要连 ZK、找 Region、建立连接。如果每次写入都新建连接,性能会很差。所以要用连接池。我一般用 HikariCP,配置如下:

HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:phoenix:zk1,zk2,zk3:2181:/hbase"); config.setMaximumPoolSize(20); config.setMinimumIdle(5); config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000);

maximumPoolSize设成 20,是因为 Phoenix 的写入是批量提交的,每个连接同时只能处理一个批次。如果并发写入的 Flink 算子有 20 个,那连接池至少要有 20 个连接。maxLifetime设成 30 分钟,是因为 Phoenix 连接长时间不用可能会被 ZK 断开,设个最大存活时间可以避免用到失效连接。

提示:如果 Flink 任务的并行度大于连接池大小,会出现连接等待。这时候要么加大连接池,要么降低并行度。我一般让连接池大小等于并行度加 5,留点余量。

4. 完整实操流程与核心环节实现

4.1 环境准备与依赖引入

先确认 HBase 和 Phoenix 的版本匹配。Phoenix 的版本必须和 HBase 的版本对应,比如 HBase 2.4.x 对应 Phoenix 5.1.x,HBase 2.5.x 对应 Phoenix 5.2.x。版本不匹配会报NoSuchMethodError或者ClassNotFoundException。

Maven 依赖:

<dependency> <groupId>org.apache.phoenix</groupId> <artifactId>phoenix-core</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-jdbc</artifactId> <version>3.1.0-1.17</version> </dependency>

Phoenix 的客户端 jar 要放到 HBase 的 lib 目录下,不然 HBase 启动时会找不到 Phoenix 的协处理器。具体来说,把phoenix-server-hbase-2.4-5.1.2.jar放到$HBASE_HOME/lib下,然后重启 HBase。

4.2 Phoenix 建表与索引创建

建表的时候要注意,主键列必须加NOT NULL,不然 Phoenix 会报错。盐值表要设SALT_BUCKETS,不然数据会热点。

CREATE TABLE IF NOT EXISTS user_behavior ( user_id VARCHAR NOT NULL, event_time BIGINT NOT NULL, event_type VARCHAR, event_detail VARCHAR, create_time TIMESTAMP, CONSTRAINT pk PRIMARY KEY (user_id, event_time) ) SALT_BUCKETS = 16, COMPRESSION = 'SNAPPY';

COMPRESSION = 'SNAPPY'是压缩算法,Snappy 压缩比不错,而且压缩解压速度快,适合实时场景。如果对存储空间更敏感,可以用GZ,但压缩解压会慢一些。

建索引:

CREATE LOCAL INDEX IF NOT EXISTS idx_event_type_time ON user_behavior (event_type, event_time);

建完索引后,可以用!indexes user_behavior命令查看索引状态。如果索引状态是BUILDING,说明还在建,等变成ACTIVE才能用。

4.3 Flink 任务编写:从 Kafka 到 Phoenix

Flink 任务的整体流程是:Kafka Source -> JSON 解析 -> 字段校验 -> JdbcSink 写入 Phoenix。我以 Flink 1.17 为例,写一个完整的示例。

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.enableCheckpointing(60000); KafkaSource<String> source = KafkaSource.<String>builder() .setBootstrapServers("kafka1:9092,kafka2:9092") .setTopics("user_behavior_topic") .setGroupId("flink_phoenix_group") .setStartingOffsets(OffsetsInitializer.latest()) .setValueOnlyDeserializer(new SimpleStringSchema()) .build(); DataStream<String> stream = env.fromSource(source, WatermarkStrategy.noWatermarks(), "Kafka Source"); DataStream<UserBehavior> parsed = stream.map(new RichMapFunction<String, UserBehavior>() { @Override public UserBehavior map(String value) throws Exception { JSONObject json = JSON.parseObject(value); UserBehavior behavior = new UserBehavior(); behavior.setUserId(json.getString("user_id")); behavior.setEventTime(json.getLong("event_time")); behavior.setEventType(json.getString("event_type")); behavior.setEventDetail(json.getString("event_detail")); return behavior; } }); String insertSql = "UPSERT INTO user_behavior (user_id, event_time, event_type, event_detail) VALUES (?, ?, ?, ?)"; JdbcExecutionOptions execOptions = JdbcExecutionOptions.builder() .withBatchSize(1000) .withBatchIntervalMs(200) .withMaxRetries(3) .build(); JdbcConnectionOptions connOptions = new JdbcConnectionOptions.JdbcConnectionOptionsBuilder() .withUrl("jdbc:phoenix:zk1,zk2,zk3:2181:/hbase") .withDriverName("org.apache.phoenix.jdbc.PhoenixDriver") .build(); parsed.addSink(JdbcSink.sink( insertSql, (ps, behavior) -> { ps.setString(1, behavior.getUserId()); ps.setLong(2, behavior.getEventTime()); ps.setString(3, behavior.getEventType()); ps.setString(4, behavior.getEventDetail()); }, execOptions, connOptions )); env.execute("Flink Phoenix HBase Sink Job");

这里用的是UPSERT而不是INSERT,因为 Phoenix 的UPSERT是幂等的,同一条数据重复写入不会报错,只会覆盖。这对 Flink 的 Exactly-Once 语义很重要,因为 Checkpoint 恢复时可能会重放一部分数据。

4.4 参数调优与性能验证

写入性能的瓶颈通常在 Phoenix 的批量提交和 HBase 的 Region 分布。我一般会调这几个参数:

参数默认值建议值说明
phoenix.mutate.batchSize10001000-5000每批提交条数,太大延迟高
phoenix.mutate.maxSize5000001000000客户端缓存的最大记录数
hbase.client.write.buffer2MB4MBHBase 客户端写缓冲区
hbase.regionserver.handler.count3060RegionServer 处理线程数

验证性能的时候,我用的是 Flink 的 Metrics 和 Phoenix 的EXPLAIN。Flink 的numRecordsOut可以看到写入速率,Phoenix 的EXPLAIN可以看到查询是否走索引。实测下来,单并行度写入 QPS 大概在 3000-5000,20 个并行度可以到 6 万-10 万。查询延迟在 20-50ms 之间,取决于数据量和索引命中率。

注意:如果写入 QPS 突然下降,先看 HBase 的 Region 是否有热点。用hbck命令检查 Region 分布,如果某个 Region 的请求量特别大,说明盐值没起作用,或者盐值分布不均匀。

5. 常见问题与排查技巧实录

5.1 连接超时与 ZK 会话过期

现象:Flink 任务运行一段时间后报ConnectionLossException或SessionExpiredException。

原因:Phoenix 客户端和 ZK 的会话超时了。默认会话超时是 60 秒,如果网络抖动或者 ZK 负载高,会话可能会过期。

解决:加大 ZK 会话超时时间,在 JDBC URL 里加参数:

jdbc:phoenix:zk1,zk2,zk3:2181:/hbase;zookeeper.session.timeout=120000

同时检查 ZK 的maxSessionTimeout配置,确保它大于客户端的会话超时时间。

5.2 写入报错 “Table is not online”

现象:Phoenix 写入时报TableNotFoundException或Table is not online。

原因:Phoenix 的表还在BUILDING状态,或者 HBase 的 Region 还没分配好。

解决:用!indexes user_behavior查看索引状态,等变成ACTIVE。如果是 Region 没分配好,用hbase hbck -repair修复。

5.3 查询不走索引

现象:EXPLAIN显示FULL SCAN,查询很慢。

原因:查询条件不匹配索引前缀,或者用了函数导致索引失效。

解决:检查查询条件,确保包含索引的第一个字段。如果必须用函数,考虑建函数索引:

CREATE INDEX idx_upper_event_type ON user_behavior (UPPER(event_type));

5.4 数据写入后查不到

现象:Flink 显示写入成功,但 Phoenix 查询查不到数据。

原因:可能是 RowKey 设计问题,或者盐值不匹配。Phoenix 的盐值表在查询时会自动加盐,但如果你用 HBase API 直接写数据,没加盐,Phoenix 就查不到。

解决:统一用 Phoenix 的 JDBC 写入,不要混用 HBase API。如果必须混用,要自己算盐值,并确保和 Phoenix 的盐值算法一致。

5.5 常见问题速查表

问题可能原因排查方法解决方案
连接超时ZK 会话过期看 Flink 日志加大会话超时
写入慢批量太小看 Flink Metrics加大 batchSize
查询慢没走索引用 EXPLAIN调整索引或查询条件
数据查不到盐值不匹配对比 RowKey统一写入方式
任务失败Phoenix 挂了看 Phoenix 日志加重试和 Checkpoint

提示:遇到问题先看日志,Flink 的日志在$FLINK_HOME/log下,Phoenix 的日志在$HBASE_HOME/logs下。日志里通常有详细的错误堆栈,比瞎猜快得多。

6. 实操心得与避坑经验

6.1 盐值不是越多越好

我一开始把SALT_BUCKETS设成 64,觉得这样数据分布更均匀。结果查询的时候,因为要跨 64 个 Region 扫描,延迟反而变高了。后来改成 16,查询延迟降了一半。盐值的数量应该和 Region 数量匹配,一般是 Region 数量的 1-2 倍。如果 Region 只有 8 个,盐值设 16 就够了,设 64 只会增加扫描开销。

6.2 索引不是越多越好

索引能加速查询,但会拖慢写入。我试过在一张表上建 5 个索引,写入 QPS 从 5 万掉到 1 万。后来砍到 2 个索引,写入恢复到 4 万。索引的维护成本是写放大的主要来源,每建一个索引,写入时就要多写一张索引表。所以索引要精,不要多。只建查询频率最高的那几个字段的索引。

6.3 Checkpoint 配置很关键

Flink 的 Checkpoint 是保证 Exactly-Once 的核心。如果 Checkpoint 间隔太长,任务失败后要重放很多数据;如果太短,又会增加开销。我一般设成 1 分钟。同时要确保 Checkpoint 的存储路径可靠,比如用 HDFS 或者对象存储。如果 Checkpoint 失败,任务会一直重启,这时候要看 Checkpoint 的日志,通常是存储路径权限问题或者网络问题。

6.4 监控要到位

这套组合涉及三个组件,任何一个出问题都会影响整条链路。我一般会监控这几个指标:Flink 的numRecordsIn和numRecordsOut,看数据有没有积压;Phoenix 的numSlowQueries,看有没有慢查询;HBase 的RegionServer的readRequestsCount和writeRequestsCount,看有没有热点。监控工具用 Prometheus + Grafana 就行,Flink 和 HBase 都有现成的 Exporter。

6.5 版本兼容性是最大的坑

Phoenix 和 HBase 的版本必须严格匹配,差一个小版本都可能出问题。我踩过一次坑:HBase 是 2.4.6,Phoenix 用了 5.1.0,结果启动时报NoSuchMethodError。后来换成 5.1.2 才正常。所以升级的时候,一定要先查 Phoenix 的官方兼容性矩阵,确认版本匹配再动手。

这套 Flink-Phoenix-HBase 的组合,我前后调了大概两个月,从最初的写入延迟 2 秒,优化到现在的 200ms,查询从全表扫描优化到索引命中。中间踩的坑不少,但跑通之后确实很稳。如果你也在做类似的实时链路,希望这些经验能帮你少走点弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 9:26:38

用Claude Code模板提升AI编程效率的实战指南

用 Claude Code 写代码这件事&#xff0c;我前后折腾了两个多月。最开始就是把它当成一个附赠上下文能力的终端助手&#xff1a;打开项目目录&#xff0c;把问题往对话框里一贴&#xff0c;让它生成函数、补测试、修修 bug。后来慢慢发现&#xff0c;同样一个工具&#xff0c;裸…

作者头像 李华
网站建设 2026/10/17 9:25:58

Pylint与Flake8实战:从默认配置到团队落地代码质量门禁

前一阵子帮一个同事审查他负责的老模块&#xff0c;点开那个超过一千行的核心函数时&#xff0c;我习惯性地问了一句&#xff1a;“你们本地跑过Pylint和Flake8吗&#xff1f;”同事说“装过&#xff0c;但默认配置一跑就是几百条警告&#xff0c;太吵了&#xff0c;后来谁也没…

作者头像 李华
网站建设 2026/10/17 9:25:54

Oracle数据库进阶实战:事务锁机制、SQL调优与PL/SQL开发

上一讲我们把Oracle的安装配置、实例架构、表空间和基础增删改查这些地基性的东西过了一遍。这次的内容算是“基础2”&#xff0c;重点会放在真正干活时躲不开的那些操作上&#xff1a;事务和锁的处理、子查询和集合运算的进阶玩法、PL/SQL里过程函数包的实战套路、还有当查询变…

作者头像 李华
网站建设 2026/10/17 9:25:35

ArXiv CV论文日更工作流:urllib+正则+Pandoc+Git轻量实践

1. 这不是“论文搬运工”&#xff0c;而是一套可复用的CV论文日更工作流你有没有试过每天早上打开ArXiv&#xff0c;想快速扫一眼计算机视觉领域的新动向&#xff0c;结果被满屏的PDF链接、混乱的标题格式、缺失的摘要、没有分类的列表搞得头大&#xff1f;我做过三年CV方向的算…

作者头像 李华
网站建设 2026/10/17 9:24:27

Blender+MCP+VS Code Copilot:用自然语言驱动AI完成3D建模与渲染

这段时间&#xff0c;大家聊得最多的就是把AI接到三维软件里。前阵子我搭了一套“Blender MCP Server VS Code Copilot”的组合&#xff0c;上手之后发现&#xff0c;以前需要在Blender里手动点半天、或者复制粘贴一长串Python代码才能完成的活&#xff0c;现在直接打字告诉A…

作者头像 李华
网站建设 2026/10/17 9:24:01

如何读懂TcpQuality检测报告:三网回程、丢包率与延迟指标逐项拆解

【免费下载链接】TcpQuality 项目地址&#xff1a; https://gitcode.com/gh_mirrors/tc/TcpQuality 点击查看 免费下载 在服务器上跑完 TcpQuality 后&#xff0c;你会得到一份包含三网回程线路、丢包率、平均延迟、TCP 重传等指标的检测报告。很多人看到满屏的数字和线路标签…

作者头像 李华