doris:基于 Arrow Flight SQL 的高速数据传输链路

Doris 基于 Arrow Flight SQL 协议实现了高速数据链路,支持多种语言使用 SQL 从 Doris 高速读取大批量数据。

用途​

从 Doris 加载大批量数据到其他组件,如 Python/Java/Spark/Flink,可以使用基于 Arrow Flight SQL 的 ADBC/JDBC 替代过去的 JDBC/PyMySQL/Pandas 来获得更高的读取性能,这在数据科学、数据湖分析等场景中经常遇到。

Apache Arrow Flight SQL 是一个由 Apache Arrow 社区开发的与数据库系统交互的协议,用于 ADBC 客户端使用 Arrow 数据格式与实现了 Arrow Flight SQL 协议的数据库交互,具有 Arrow Flight 的速度优势以及 JDBC/ODBC 的易用性。

Doris 支持 Arrow Flight SQL 的动机、设计与实现、性能测试结果、以及有关 Arrow Flight、ADBC 的更多概念可以看 GitHub Issue,这篇文档主要介绍 Doris Arrow Flight SQL 的使用方法,以及一些常见问题。

安装 Apache Arrow 你可以去官方文档( Apache Arrow)找到详细的安装教程。

Python 使用方法​

使用 Python 的 ADBC Driver 连接 Doris 实现数据的极速读取,下面的步骤使用 Python(版本 >= 3.9)的 ADBC Driver 执行一系列常见的数据库语法操作,包括 DDL、DML、设置 Session 变量以及 Show 语句等。

安装 Library​

Library 被发布在 PyPI,可通过以下方式简单安装:

pip install adbc_driver_manager
pip install adbc_driver_flightsql

在代码中import 以下模块/库来使用已安装的 Library:

import adbc_driver_manager
import adbc_driver_flightsql.dbapi as flight_sql>>> print(adbc_driver_manager.__version__)
1.1.0
>>> print(adbc_driver_flightsql.__version__)
1.1.0

连接 Doris​

创建与 Doris Arrow Flight SQL 服务交互的客户端。需提供 Doris FE 的 Host、Arrow Flight Port 、登陆用户名以及密码,并进行以下配置。 修改 Doris FE 和 BE 的配置参数:

  • 修改fe/conf/fe.conf 中 arrow_flight_sql_port 为一个可用端口,如 9090。
  • 修改 be/conf/be.conf中 arrow_flight_sql_port 为一个可用端口,如 9091。

注: fe.conf 与 be.conf 中配置的 arrow_flight_sql_port 不相同

假设 Doris 实例中 FE 和 BE 的 Arrow Flight SQL 服务将分别在端口 9090 和 9091 上运行,且 Doris 用户名/密码为“user”/“pass”,那么连接过程如下所示:

conn = flight_sql.connect(uri="grpc://{FE_HOST}:{fe.conf:arrow_flight_sql_port}", db_kwargs={adbc_driver_manager.DatabaseOptions.USERNAME.value: "user",adbc_driver_manager.DatabaseOptions.PASSWORD.value: "pass",})
cursor = conn.cursor()

连接完成后,可以通过 SQL 使返回的 Cursor 与 Doris 交互,执行例如建表、获取元数据、导入数据、查询等操作。

建表与获取元数据​

将 Query 传递给 cursor.execute()函数,执行建表与获取元数据操作:

cursor.execute("DROP DATABASE IF EXISTS arrow_flight_sql FORCE;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("create database arrow_flight_sql;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("show databases;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("use arrow_flight_sql;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("""CREATE TABLE arrow_flight_sql_test(k0 INT,k1 DOUBLE,K2 varchar(32) NULL DEFAULT "" COMMENT "",k3 DECIMAL(27,9) DEFAULT "0",k4 BIGINT NULL DEFAULT '10',k5 DATE,)DISTRIBUTED BY HASH(k5) BUCKETS 5PROPERTIES("replication_num" = "1");""")
print(cursor.fetchallarrow().to_pandas())cursor.execute("show create table arrow_flight_sql_test;")
print(cursor.fetchallarrow().to_pandas())

如果 StatusResult 返回 0 ,则说明 Query 执行成功(这样设计的原因是为了兼容 JDBC)。

  StatusResult
0            0StatusResult
0            0Database
0         __internal_schema
1          arrow_flight_sql
..                      ...
507             udf_auth_db[508 rows x 1 columns]StatusResult
0            0StatusResult
0            0Table                                       Create Table
0  arrow_flight_sql_test  CREATE TABLE `arrow_flight_sql_test` (\n  `k0`...

导入数据​

执行 INSERT INTO,向所创建表中导入少量测试数据:

cursor.execute("""INSERT INTO arrow_flight_sql_test VALUES('0', 0.1, "ID", 0.0001, 9999999999, '2023-10-21'),('1', 0.20, "ID_1", 1.00000001, 0, '2023-10-21'),('2', 3.4, "ID_1", 3.1, 123456, '2023-10-22'),('3', 4, "ID", 4, 4, '2023-10-22'),('4', 122345.54321, "ID", 122345.54321, 5, '2023-10-22');""")
print(cursor.fetchallarrow().to_pandas())

如下所示则证明导入成功:

  StatusResult
0            0

如果需要导入大批量数据到 Doris,可以使用 pydoris 执行 Stream Load 来实现。

执行查询​

接着对上面导入的表进行查询查询,包括聚合、排序、Set Session Variable 等操作。

cursor.execute("select * from arrow_flight_sql_test order by k0;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("set exec_mem_limit=2000;")
print(cursor.fetchallarrow().to_pandas())cursor.execute("show variables like \"%exec_mem_limit%\";")
print(cursor.fetchallarrow().to_pandas())cursor.execute("select k5, sum(k1), count(1), avg(k3) from arrow_flight_sql_test group by k5;")
print(cursor.fetchallarrow().to_pandas())

结果如下所示:

   k0            k1    K2                k3          k4          k5
0   0       0.10000    ID       0.000100000  9999999999  2023-10-21
1   1       0.20000  ID_1       1.000000010           0  2023-10-21
2   2       3.40000  ID_1       3.100000000      123456  2023-10-22
3   3       4.00000    ID       4.000000000           4  2023-10-22
4   4  122345.54321    ID  122345.543210000           5  2023-10-22[5 rows x 6 columns]StatusResult
0            0Variable_name Value Default_Value Changed
0  exec_mem_limit  2000    2147483648       1k5  Nullable(Float64)_1  Int64_2 Nullable(Decimal(38, 9))_3
0  2023-10-22         122352.94321        3            40784.214403333
1  2023-10-21              0.30000        2                0.500050005[2 rows x 5 columns]

完整代码​

# Doris Arrow Flight SQL Test# step 1, library is released on PyPI and can be easily installed.
# pip install adbc_driver_manager
# pip install adbc_driver_flightsql
import adbc_driver_manager
import adbc_driver_flightsql.dbapi as flight_sql# step 2, create a client that interacts with the Doris Arrow Flight SQL service.
# Modify arrow_flight_sql_port in fe/conf/fe.conf to an available port, such as 9090.
# Modify arrow_flight_sql_port in be/conf/be.conf to an available port, such as 9091.
conn = flight_sql.connect(uri="grpc://{FE_HOST}:{fe.conf:arrow_flight_sql_port}", db_kwargs={adbc_driver_manager.DatabaseOptions.USERNAME.value: "root",adbc_driver_manager.DatabaseOptions.PASSWORD.value: "",})
cursor = conn.cursor()# interacting with Doris via SQL using Cursor
def execute(sql):print("\n### execute query: ###\n " + sql)cursor.execute(sql)print("### result: ###")print(cursor.fetchallarrow().to_pandas())# step3, execute DDL statements, create database/table, show stmt.
execute("DROP DATABASE IF EXISTS arrow_flight_sql FORCE;")
execute("show databases;")
execute("create database arrow_flight_sql;")
execute("show databases;")
execute("use arrow_flight_sql;")
execute("""CREATE TABLE arrow_flight_sql_test(k0 INT,k1 DOUBLE,K2 varchar(32) NULL DEFAULT "" COMMENT "",k3 DECIMAL(27,9) DEFAULT "0",k4 BIGINT NULL DEFAULT '10',k5 DATE,)DISTRIBUTED BY HASH(k5) BUCKETS 5PROPERTIES("replication_num" = "1");""")
execute("show create table arrow_flight_sql_test;")# step4, insert into
execute("""INSERT INTO arrow_flight_sql_test VALUES('0', 0.1, "ID", 0.0001, 9999999999, '2023-10-21'),('1', 0.20, "ID_1", 1.00000001, 0, '2023-10-21'),('2', 3.4, "ID_1", 3.1, 123456, '2023-10-22'),('3', 4, "ID", 4, 4, '2023-10-22'),('4', 122345.54321, "ID", 122345.54321, 5, '2023-10-22');""")# step5, execute queries, aggregation, sort, set session variable
execute("select * from arrow_flight_sql_test order by k0;")
execute("set exec_mem_limit=2000;")
execute("show variables like \"%exec_mem_limit%\";")
execute("select k5, sum(k1), count(1), avg(k3) from arrow_flight_sql_test group by k5;")# step6, close cursor 
cursor.close()

JDBC Connector with Arrow Flight SQL​

Arrow Flight SQL 协议的开源 JDBC 驱动兼容标准的 JDBC API,可用于大多数 BI 工具通过 JDBC 访问 Doris,并支持高速传输 Apache Arrow 数据。使用方法与通过 MySQL 协议的 JDBC 驱动连接 Doris 类似,只需将链接 URL 中的 jdbc:mysql 协议换成 jdbc:arrow-flight-sql 协议,查询返回的结果依然是 JDBC 的 ResultSet 数据结构。

POM dependency:

<properties><arrow.version>15.0.1</arrow.version>
</properties>
<dependencies><dependency><groupId>org.apache.arrow</groupId><artifactId>flight-sql-jdbc-core</artifactId><version>${arrow.version}</version></dependency>
</dependencies>

使用 Java 9 或更高版本时,必须通过在 Java 命令中添加 --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED 来暴露某些 JDK 内部结构:

# Directly on the command line
$ java --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED -jar ...
# Indirectly via environment variables
$ env _JAVA_OPTIONS="--add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED" java -jar ...

否则,您可能会看到一些错误,如 module java.base does not "opens java.nio" to unnamed module 或者 module java.base does not "opens java.nio" to org.apache.arrow.memory.core 或者 ava.lang.NoClassDefFoundError: Could not initialize class org.apache.arrow.memory.util.MemoryUtil (Internal; Prepare)

如果您在 IntelliJ IDEA 中调试,需要在 Run/Debug Configurations 的 Build and run 中增加 --add-opens=java.base/java.nio=ALL-UNNAMED,参照下面的图片:

IntelliJ IDEA

连接代码示例如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;Class.forName("org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver");
String DB_URL = "jdbc:arrow-flight-sql://{FE_HOST}:{fe.conf:arrow_flight_sql_port}?useServerPrepStmts=false"+ "&cachePrepStmts=true&useSSL=false&useEncryption=false";
String USER = "root";
String PASS = "";Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet resultSet = stmt.executeQuery("select * from information_schema.tables;");
while (resultSet.next()) {System.out.println(resultSet.toString());
}resultSet.close();
stmt.close();
conn.close();

Java 使用方法​

除了使用 JDBC,与 Python 类似,Java 也可以创建 Driver 读取 Doris 并返回 Arrow 格式的数据,下面分别是使用 AdbcDriver 和 JdbcDriver 连接 Doris Arrow Flight Server。

POM dependency:

<properties><adbc.version>0.12.0</adbc.version>
</properties><dependencies><dependency><groupId>org.apache.arrow.adbc</groupId><artifactId>adbc-driver-jdbc</artifactId><version>${adbc.version}</version></dependency><dependency><groupId>org.apache.arrow.adbc</groupId><artifactId>adbc-core</artifactId><version>${adbc.version}</version></dependency><dependency><groupId>org.apache.arrow.adbc</groupId><artifactId>adbc-driver-manager</artifactId><version>${adbc.version}</version></dependency><dependency><groupId>org.apache.arrow.adbc</groupId><artifactId>adbc-sql</artifactId><version>${adbc.version}</version></dependency><dependency><groupId>org.apache.arrow.adbc</groupId><artifactId>adbc-driver-flight-sql</artifactId><version>${adbc.version}</version></dependency>
</dependencies>

ADBC Driver​

连接代码示例如下:

// 1. new driver
final BufferAllocator allocator = new RootAllocator();
FlightSqlDriver driver = new FlightSqlDriver(allocator);
Map<String, Object> parameters = new HashMap<>();
AdbcDriver.PARAM_URI.set(parameters, Location.forGrpcInsecure("{FE_HOST}", {fe.conf:arrow_flight_sql_port}).getUri().toString());
AdbcDriver.PARAM_USERNAME.set(parameters, "root");
AdbcDriver.PARAM_PASSWORD.set(parameters, "");
AdbcDatabase adbcDatabase = driver.open(parameters);// 2. new connection
AdbcConnection connection = adbcDatabase.connect();
AdbcStatement stmt = connection.createStatement();// 3. execute query
stmt.setSqlQuery("select * from information_schema.tables;");
QueryResult queryResult = stmt.executeQuery();
ArrowReader reader = queryResult.getReader();// 4. load result
List<String> result = new ArrayList<>();
while (reader.loadNextBatch()) {VectorSchemaRoot root = reader.getVectorSchemaRoot();String tsvString = root.contentToTSVString();result.add(tsvString);
}
System.out.printf("batchs %d\n", result.size());// 5. close
reader.close();
queryResult.close();
stmt.close();
connection.close();

JDBC Driver​

连接代码示例如下:

final Map<String, Object> parameters = new HashMap<>();
AdbcDriver.PARAM_URI.set(parameters,"jdbc:arrow-flight-sql://{FE_HOST}:{fe.conf:arrow_flight_sql_port}?useServerPrepStmts=false&cachePrepStmts=true&useSSL=false&useEncryption=false");
AdbcDriver.PARAM_USERNAME.set(parameters, "root");
AdbcDriver.PARAM_PASSWORD.set(parameters, "");
try (BufferAllocator allocator = new RootAllocator();AdbcDatabase db = new JdbcDriver(allocator).open(parameters);AdbcConnection connection = db.connect();AdbcStatement stmt = connection.createStatement()
) {stmt.setSqlQuery("select * from information_schema.tables;");AdbcStatement.QueryResult queryResult = stmt.executeQuery();ArrowReader reader = queryResult.getReader();List<String> result = new ArrayList<>();while (reader.loadNextBatch()) {VectorSchemaRoot root = reader.getVectorSchemaRoot();String tsvString = root.contentToTSVString();result.add(tsvString);}long etime = System.currentTimeMillis();System.out.printf("batchs %d\n", result.size());reader.close();queryResult.close();stmt.close();
}  catch (Exception e) {e.printStackTrace();
}

JDBC 和 Java 连接方式的选择​

对比传统的 jdbc:mysql 连接方式,Jdbc 和 Java 的 Arrow Flight SQL 连接方式的性能测试见 GitHub Issue,这里基于测试结论给出一些使用建议。

  1. 上述三种 Java Arrow Flight SQL 连接方式的选择上,如果后续的数据分析将基于行存的数据格式,那么使用 jdbc:arrow-flight-sql,这将返回 JDBC ResultSet 格式的数据;如果后续的数据分析可以基于 Arrow 格式或其他列存数据格式,那么使用 Flight AdbcDriver 或 Flight JdbcDriver 直接返回 Arrow 格式的数据,这将避免行列转换,并可利用 Arrow 的特性加速数据解析。

  2. 无论解析 JDBC ResultSet 还是 Arrow 格式的数据,所耗费的时间都大于读取数据的耗时,如果你那里使用 Arrow Flight SQL 的性能不符合预期,和 jdbc:mysql:// 相比提升有限,不妨分析下是否解析数据耗时太长。

  3. 对所有连接方式而言,JDK 17 都比 JDK 1.8 读取数据的速度更快。

  4. 当读取数据量非常大时,使用 Arrow Flight SQL 将比 jdbc:mysql:// 使用更少的内存,所以如果你受内存不足困扰,同样可以尝试下 Arrow Flight SQL。

  5. 除了上述三种连接方式,还可以使用原生的 FlightClient 连接 Arrow Flight Server,可以更加灵活的并行读取多个 Endpoints。Flight AdbcDriver也是基于 FlightClient 创建的链接,相较于直接使用 FlightClient 更简单。

与其他大数据组件交互​

Arrow Flight 官方目前没有支持 Spark 和 Flink 的计划(见 GitHub Issue),Doris Spark Connector 和 Doris Flink Connector 目前还不支持通过 Arrow Flight SQL 访问 Doris。其中 Doris Flink Connector 支持 Arrow Flight SQL 正在开发中,预期能提升数倍读取性能。

社区之前参考开源的 Spark-Flight-Connector,在 Spark 中使用 FlightClient 连接 Doris 测试,发现 Arrow 与 Doris Block 之间数据格式转换的速度更快,是 CSV 格式与 Doris Block 之间转换速度的 10 倍,而且对 Map,Array 等复杂类型的支持更好,这是因为Arrow 数据格式的压缩率高,传输时网络开销小。不过目前 Doris Arrow Flight 还没有实现多节点并行读取,仍是将查询结果汇总到一台 BE 节点后返回,对简单的批量导出数据而言,性能可能没有 Doris Spark Connector 快,后者支持 Tablet 级别的并行读取。如果你希望在 Spark 使用 Arrow Flight SQL 连接 Doris,可以参考开源的 Spark-Flight-Connector 和 Dremio-Flight-Connector 自行实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65900.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据挖掘——关联规则挖掘

数据挖掘——关联数据挖掘 关联数据挖掘关联规则关联规则挖掘问题&#xff1a;具体挖掘过程Apriori 产生关联规则 关联数据挖掘 关联分析用于发现隐藏在大型数据集中的令人感兴趣的联系&#xff0c;所发现的模式通常用关联规则或频繁项集的形式表示。 关联规则反映一个事物与…

DVWA靶场Brute Force (暴力破解) 漏洞low(低),medium(中等),high(高),impossible(不可能的)所有级别通关教程

目录 暴力破解low方法1方法2 mediumhighimpossible 暴力破解 暴力破解是一种尝试通过穷尽所有可能的选项来获取密码、密钥或其他安全凭证的攻击方法。它是一种简单但通常无效率的破解技术&#xff0c;适用于密码强度较弱的环境或当攻击者没有其他信息可供利用时。暴力破解的基…

[react] 纯组件优化子

有组件如下,上面变化秒数, 下面是大量计算的子组件,上面每一秒钟变化一次,这时候子组件会不断重新渲染, 浪费资源 父组件如下 import React, { memo, useEffect, useMemo, useState } from react; import type { ReactNode, FC } from react; import HugeCount from ./Te; int…

CSS进阶和SASS

目录 一、CSS进阶 1.1、CSS变量 1.2、CSS属性值的计算过程 1.3、做杯咖啡 1.4、下划线动画 1.5、CSS中的混合模式(Blending) 二、SASS 2.1、Sass的颜色函数 2.2、Sass的扩展(extend)和占位符(%)、混合(Mixin) 2.3、Sass的数学函数 2.4、Sass的模块化开发 2.5、Sass…

使用 Spring Boot 和 GraalVM 的原生镜像

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;历代文学&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编程&#xff0c;高并发设计&#xf…

计算机网络-L2TP VPN基础实验配置

一、概述 上次大概了解了L2TP的基本原理和使用场景&#xff0c;今天来模拟一个小实验&#xff0c;使用Ensp的网卡桥接到本地电脑试下L2TP拨号&#xff0c;今天主要使用标准的L2TP&#xff0c;其实在这个基础上可以加上IPSec进行加密&#xff0c;提高安全性。 拓扑说明&#xf…

Linux | 零基础Ubuntu解压RaR等压缩包文件

目录 介绍 案例分析 安装工具 解压实践 介绍 RAR是一种专利文件格式&#xff0c;用于数据压缩与归档打包&#xff0c;开发者为尤金罗谢尔&#xff08;俄语&#xff1a;Евгений Лазаревич Рошал&#xff0c;拉丁转写&#xff1a;Yevgeny Lazarevich R…

Postman接口测试05|实战项目笔记

目录 一、项目接口概况 二、单接口测试-登录接口&#xff1a;POST 1、正例 2、反例 ①姓名未注册 ②密码错误 ③姓名为空 ④多参 ⑤少参 ⑥无参 三、批量运行测试用例 四、生成测试报告 1、Postman界面生成 2、Newman命令行生成 五、token鉴权&#xff08;“…

网络分析工具-tcpdump

文章目录 前言一、tcpdump基础官网链接命令选项详解常规过滤规则tcpdump输出 一、tcpdump实践HTTP协议ICMP状态抓包 前言 当遇到网络疑难问题的时候&#xff0c;抓包是最基本的技能&#xff0c;通过抓包才能看到网络底层的问题 一、tcpdump基础 tcpdump是一个常用的网络分析工…

可编辑31页PPT | 大数据湖仓一体解决方案

荐言分享&#xff1a;在当今数字化时代&#xff0c;大数据已成为企业决策和业务优化的关键驱动力。然而&#xff0c;传统的数据处理架构&#xff0c;如数据仓库和数据湖&#xff0c;各自存在局限性&#xff0c;难以满足企业对数据高效存储、灵活处理及实时分析的综合需求。因此…

STM32中断详解

STM32中断详解 NVIC 中断系统中断向量表相关寄存器中断优先级中断配置 外部中断实验EXTI框图外部中断/事件线映射中断步骤初始化代码实现 定时器中断通用定时器相关功能标号1&#xff1a;时钟源标号 2&#xff1a;控制器标号 3&#xff1a;时基单元 代码实现 NVIC 中断系统 STM…

【LeetCode】200、岛屿数量

【LeetCode】200、岛屿数量 文章目录 一、并查集1.1 并查集1.2 多语言解法 二、洪水填充 DFS2.1 洪水填充 DFS 一、并查集 1.1 并查集 // go var sets int var father [90000]intfunc numIslands(grid [][]byte) int {n, m : len(grid), len(grid[0])build(grid, n, m)for i …

SOME/IP 协议详解——序列化

文章目录 0. 概述1.基本数据序列化2.字符串序列化2.1 字符串通用规则2.2 固定长度字符串规则2.3 动态长度字符串规则 3.结构体序列化4. 带有标识符和可选成员的结构化数据类型5. 数组5.1 固定长度数组5.2 动态长度数组5.3 Enumeration&#xff08;枚举&#xff09;5.4 Bitfield…

【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错

一、问题原因 1.安卓安全性变更 Android 12 的安全性变更&#xff0c;Google 引入了更严格的 PendingIntent 安全管理&#xff0c;强制要求开发者明确指定 PendingIntent 的可变性&#xff08;Mutable&#xff09;或不可变性&#xff08;Immutable&#xff09;。 但是&#xf…

C之(14)gcov覆盖率

C之(14)gcov覆盖率 Author: Once Day Date: 2024年12月30日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: C语言_Once-Day的博客-CSDN博客 前些天…

简易屏幕共享工具-基于WebSocket

前面写了两个简单的屏幕共享工具&#xff0c;不过那只是为了验证通过截屏的方式是否可行&#xff0c;因为通常手动截屏的频率很低&#xff0c;而对于视频来说它的帧率要求就很高了&#xff0c;至少要一秒30帧率左右。所以&#xff0c;经过实际的截屏工具验证&#xff0c;我了解…

Paperlib(论文管理工具)

Paperlib 是一个简单好用的论文管理工具。软件接入各学科数据库用于匹配论文元数据&#xff0c;逐步为每一个学科&#xff08;例如计算机科学&#xff0c;物理学等&#xff09;定制化数据库组合提高检索精度。尤其是精准的会议论文元数据检索能力。还可以管理你的论文&#xff…

c# 2024/12/27 周五

6《详解类型、变量与对象》36 详解类型、变量与对象 _1_哔哩哔哩_bilibili

Formality:匹配(match)是如何进行的?

相关阅读Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 匹配点、对比点和逻辑锥 匹配指的是Formality工具尝试将参考设计中的每个匹配点与实现设计中的相应匹配点进行配对&#xff0c;这里的匹配点包括对比点(Compare Point…

分布式算法(五):初识ZAB协议

文章目录 一、什么是Zookeeper二、ZAB与Zookeeper的关系为什么Zookeeper不直接使用Paxos 三、ZAB简介1.名词解释提案&#xff08;Proposal&#xff09;事务&#xff08;Transaction&#xff09;原子广播&#xff08;Atomic Broadcast&#xff09; 2.集群角色领导者&#xff08;…