Clickhouse 学习(最新)
Clickhouse-23.2.1.2537 学习
一、Clickhouse概述
clickhouse 官网网址:https://clickhouse.com/

OLTP(联机事务处理系统)
例如mysql等关系型数据库,在对于存储小数据量的时候,查询数据并分析速度很快,OLTP本身其实是一个逻辑上的概念,指的是某个数据库,主要是针对增删改操作的。频繁的做增删改操作
里面的数据会经常的发生变化。
OLAP(联机分析处理系统)
指的是数据库中的数据长期不变,有着大量的历史数据,并且可以随时的做分析,而增删改操作很少。频繁的做查询操作
OLAP 种类系统架构的的特点
1、绝大多数是读请求
2、数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
3、已添加到数据库的数据不能修改。
4、对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
5、宽表,即每个表包含着大量的列
6、查询相对较少(通常每台服务器每秒查询数百次或更少)
7、对于简单查询,允许延迟大约50毫秒
8、列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
9、处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
10、事务不是必须的
11、对数据一致性要求低
12、每个查询有一个大表。除了他以外,其他的都很小。
13、查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中
二、表操作
数据类型
注意事项:
1、建表写数据类型的时候,严格区分大小写Int32,不能写成int32
2、建表的时候,必须要指定表引擎(详细引擎的使用,后面介绍)
a. 整数类型
UInt8, UInt16, UInt32, UInt64, UInt128, UInt256, Int8, Int16, Int32, Int64, Int128, Int256
Int8 — [-128 : 127]
Int16 — [-32768 : 32767]
Int32 — [-2147483648 : 2147483647]
Int64 — [-9223372036854775808 : 9223372036854775807]
Int128 — [-170141183460469231731687303715884105728 : 170141183460469231731687303715884105727]
Int256 — [-57896044618658097711785492504343953926634992332820282019728792003956564819968 : 57896044618658097711785492504343953926634992332820282019728792003956564819967]
UInt8 — [0 : 255]
UInt16 — [0 : 65535]
UInt32 — [0 : 4294967295]
UInt64 — [0 : 18446744073709551615]
UInt128 — [0 : 340282366920938463463374607431768211455]
UInt256 — [0 : 115792089237316195423570985008687907853269984665640564039457584007913129639935]
b. 字符串类型
String:可变长字符串
FixedString(长度):固定长字符串,参数是字节数,执行效率比String要高
c. 日期类型
Date 年-月-日
Date32 年-月-日
DateTime 年-月-日 时-分-秒
DateTime64 年-月-日 时-分-秒.毫秒
案例:
# 建表语句:
create table date_test (date1 Date,date2 Date32,date3 DateTime,date4 DateTime64) ENGINE = TinyLog;
# 插入语句:
insert into date_test values ('2023-11-21','2023-11-21','2023-11-21','2023-11-21');
insert into date_test values (1711435333589,1711435333589,1711435333589,1711435333589); //2024-03-26 15:33:38
d. UUID类型
clickhouse提供了一个函数:generateUUIDv4() 生成一个 00000000-0000-0000-0000-000000000000 的编号 编号的类型就是UUID类型
bee32020-a6cb-49a6-a10b-427381b11613
e. 可为空 Nullable
例如建表的时候,有一个id字段类型时Int32,如果当id不确定的时候,我们应该使用null进行填充,而不应该用默认值0,所以,我们这里应该添加的是null
Nullable(Int32)
create table test1 (id Int32,name String) ENGINE = TinyLog;
insert into students_test values (null,'张玮2','男','特训营24期');
f. 数组 Array(T)
字段类型是数组,对于同一个数组,在建表的时候指定数据类型,注意:在MergeTree表引擎中是不允许出现数组嵌套的
注意:需要使用array()函数,将元素组成数组,将来还可以使用toTypeName()查看某一列的数据类型
# 举例:
create table t1 (col1 Array(Int8)) ENGINE = TinyLog;
insert into t1 values array(11,12,13);
g. 小数类型
# Decimal(P,S),Decimal32(S),Decimal64(S),Decimal128(S)
有符号的定点数,可在加、减和乘法运算过程中保持精度。对于除法,最低有效数字会被丢弃(不舍入)
P - 精度。有效范围:[1:38],决定可以有多少个十进制数字(包括分数)。
S - 规模。有效范围:[0:P],决定数字的小数部分中包含的小数位数。
Decimal(4,2)
举例:12.12234 Decimal(7,5)
P: 7
S: 5
1、建表语句(入门案例)
-- ck安装后的数据根目录:/var/lib/clickhouse
create table users3 (id Int8,name FixedString(12),gender Nullable(FixedString(3)),clazz String) ENGINE = TinyLog;
2、插入数据
# 基本格式
INSERT INTO [db.]table [(c1, c2, c3)] VALUES (v11, v12, v13), (v21, v22, v23), ...
# 举例
insert into students_test values (1001,'陆澳','男','特训营24期'),(1002,'李佳豪','男','特训营24期'),(1003,'郭香香','女','特训营24期');
insert into students_test values (1004,'王宇杰','男','特训营24期'),(1005,'张怀远','男','特训营24期'),(1006,'史俊超','女','特训营24期');
insert into students_test (name,gender,clazz) values ('张玮','男','特训营24期');
# 查看表结构
desc 表名;

三、引擎
1、数据库引擎
1.1 Atomic
clickhouse数据库建库默认指定的数据库引擎
它支持非阻塞的DROP TABLE和RENAME TABLE查询和原子的EXCHANGE TABLES t1 AND t2查询。默认情况下使用Atomic数据库引擎。
1.2 MySQL
(1)就是以mysql数据库里面的表,然后在clickhouse里面创建一个表,和MySQL里面的表进行映射;
(2)映射过来的表,和原来MySQL数据库里面的表名字是一模一样的,数据也是一样的,
(3)对clickhouse里面的表进行插入操作,会在mysql表里面进行相应的插入数据的操作,就是会同步
(4)也可以对表进行关联的操作。
(5)删除数据只能在MySQL端删除数据,不能在clickhouse端删除,并且会同步数据到clickhouse端的表里面
MySQL引擎用于将远程的MySQL服务器中的表映射到ClickHouse中,并允许您对表进行
INSERT
和SELECT
查询,以方便您在ClickHouse与MySQL之间进行数据交换
MySQL
数据库引擎会将对其的查询转换为MySQL语法并发送到MySQL服务器中,因此您可以执行诸如SHOW TABLES
或SHOW CREATE TABLE
之类的操作。但您无法对其执行以下操作:
RENAME
CREATE TABLE
ALTER
# 在clickhouse中创建数据库并指定远程的MySQL服务,将其中的某一个数据库映射过来(就将这新建的数据库看成一个远程客户端连接了mysql)
# 建库语句
CREATE DATABASE [IF NOT EXISTS] db_name [ON CLUSTER cluster]
ENGINE = MySQL('host:port', ['database' | database], 'user', 'password')
需要指定主机名和端口号,数据库名字(注意是数据库的名字,不是表的名字),用户名和密码
# 举例
create database IF NOT EXISTS bigdata24_mysql ENGINE = MySQL('192.168.169.100:3306','bigdata24','root','123456');
# 参数理解:
host:port — MySQL服务地址,既可以是ip地址,也可以是主机名(如果是主机名,要配置hosts映射)
database — MySQL数据库名称
user — MySQL用户名
password — MySQL用户密码
# 操作的注意事项
1、mysql的数据和ck中数据库映射的数据几乎是同步的
2、在任意一端添加数据,另一端都可以观察添加后的结果
3、对于删除数据,只能在mysql端删除,不能够在ck端删除
2、表引擎
2.1 日志引擎
a. Log
Log
与TinyLog
的不同之处在于,«标记» 的小文件与列文件存在一起。这些标记写在每个数据块上,并且包含偏移量,这些偏移量指示从哪里开始读取文件以便跳过指定的行数。这使得可以在多个线程中读取表数据。对于并发数据访问,可以同时执行读取操作,而写入操作则阻塞读取和其它写入。Log
引擎不支持索引。同样,如果写入表失败,则该表将被破坏,并且从该表读取将返回错误。Log
引擎适用于临时数据,write-once 表以及测试或演示目的。
# 建表,指定表引擎为Log
create table students_log (id Int32,name String,gender FixedString(3),clazz String) ENGINE = Log;
# 添加数据
insert into students_log values (1001,'尚平','男','特训营29期'),(1002,'丁义杰','男','特训营29期'),(1003,'汪权','男','特训营29期');
b. TinyLog
最简单的表引擎,用于将数据存储在磁盘上。每列都存储在单独的压缩文件中。写入时,数据将附加到文件末尾。
并发数据访问不受任何限制:
- 如果同时从表中读取并在不同的查询中写入,则读取操作将抛出异常
- 如果同时写入多个查询中的表,则数据将被破坏。
这种表引擎的典型用法是 write-once:首先只写入一次数据,然后根据需要多次读取。查询在单个流中执行。换句话说,此引擎适用于相对较小的表(建议最多1,000,000行)。如果您有许多小表,则使用此表引擎是适合的,因为它比Log引擎更简单(需要打开的文件更少)。当您拥有大量小表时,可能会导致性能低下,但在可能已经在其它 DBMS 时使用过,则您可能会发现切换使用 TinyLog 类型的表更容易。不支持索引。
c. StripeLog
写数据:
StripeLog
引擎将所有列存储在一个文件中。对每一次Insert
请求,ClickHouse 将数据块追加在表文件的末尾,逐列写入。ClickHouse 为每张表写入以下文件:
data.bin
— 数据文件。index.mrk
— 带标记的文件。标记包含了已插入的每个数据块中每列的偏移量。
StripeLog
引擎不支持ALTER UPDATE
和ALTER DELETE
操作。读取数据:
带标记的文件使得 ClickHouse 可以并行的读取数据。这意味着
SELECT
请求返回行的顺序是不可预测的。使用ORDER BY
子句对行进行排序。
CREATE TABLE stripe_log_table(timestamp DateTime,message_type String,message String) ENGINE = StripeLog
INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The first regular message')
INSERT INTO stripe_log_table VALUES (now(),'REGULAR','The second regular message'),(now(),'WARNING','The first warning message')
# 建表,指定表引擎为Log
create table students_stripelog (id Int32,name String,gender FixedString(3),clazz String) ENGINE = StripeLog;
# 添加数据
insert into students_stripelog values (1001,'陆澳','男','特训营24期'),(1002,'李佳豪','男','特训营24期'),(1003,'郭香香','女','特训营24期');
2.2 合并树家族
MergeTree
Clickhouse 中最强大的表引擎当属
MergeTree
(合并树)引擎及该系列(*MergeTree
)中的其他引擎。
MergeTree
系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。主要特点:
# 建表语句规范:
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
...
INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1,
INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2
) ENGINE = MergeTree()
ORDER BY expr
[PARTITION BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...]
[SETTINGS name=value, ...]
# 案例
# 建表语句
create table goods_orders (id String,uname String,goods_name String,price Int64,date Date32) ENGINE = MergeTree() order by date PARTITION BY date;
# 插入语句
insert into goods_orders values ('1001','易政','oppo手机',7000,'2023-11-21'),('1002','盛宇豪','机械革命电脑',10000,'2023-11-22'),('1003','陆云龙','iphone14',5000,'2023-11-21'),('1004','雷彭程','AI吸尘器',17000,'2023-11-22');
insert into goods_orders values ('1005','小虎','小天才手表',8000,'2023-11-22'),('1006','谷圆圆','投影仪',20000,'2023-11-21'),('1007','彭','水杯',20,'2023-11-22'),('1008','黄磊','iwatch',3000,'2023-11-21');
注意:默认是针对每一批数据按照分区字段的值进行分区

根据上图所示,并不会立刻的将所有的相同的分区进行合并,如果想要很快的看到结果,可以手动的进行合并
optimize table 表名 final;
optimize table goods_orders final;
手动合并后结果/或者是过一段时间自动合并结果

今后开发的时候,常用的表引擎:针对数据量小的表引擎用TinyLog, 数据量大表引擎就用MergeTree
四、常用函数
4.1 算术函数
对于所有算术函数,结果类型为结果适合的最小数值类型(如果存在这样的类型)。最小数值类型是根据数值的位数,是否有符号以及是否是浮点类型而同时进行的。如果没有足够的位,则采用最高位类型。简单理解:会自动的根据我们的数值大小,来选用最适合的数据类型存储。
# plus(a, b), a + b operator
计算数值的总和。 您还可以将Date或DateTime与整数进行相加。在Date的情况下,和整数相加整数意味着添加相应的天数。对于DateTime,这意味着添加相应的秒数。
# minus(a, b), a - b operator
计算数值之间的差,结果总是有符号的。
您还可以将Date或DateTime与整数进行相减。见上面的’plus’。
# multiply(a, b), a * b operator
计算数值的乘积。
# divide(a, b), a / b operator
计算数值的商。结果类型始终是浮点类型。 它不是整数除法。对于整数除法,请使用’intDiv’函数。 当除以零时,你得到’inf’,‘- inf’或’nan’。
# intDiv(a,b)
计算数值的商,向下舍入取整(按绝对值)。 除以零或将最小负数除以-1时抛出异常。
# max2(a,b)
value1 — 第一个值,类型为Int/UInt或Float。
value2 — 第二个值,类型为Int/UInt或Float。
# max2(value1, value2)
value1 — 第一个值,类型为Int/UInt or Float。
value2 — 第二个值,类型为Int/UInt or Float。

4.2 比较函数
比较函数始终返回0或1(UInt8)。
可以比较以下类型:
- 数字
- String 和 FixedString
- 日期
- 日期时间
以上每个组内的类型均可互相比较,但是对于不同组的类型间不能够进行比较。
例如,您无法将日期与字符串进行比较。您必须使用函数将字符串转换为日期,反之亦然。
字符串按字节进行比较。较短的字符串小于以其开头并且至少包含一个字符的所有字符串。
等于,a=b和a==b 运算符
不等于,a!=b和a<>b 运算符
少, < 运算符
大于, > 运算符
小于等于, <= 运算符
大于等于, >= 运算符
4.3 数据类型转换
当你把一个值从一个类型转换为另外一个类型的时候,你需要注意的是这是一个不安全的操作,可能导致数据的丢失。数据丢失一般发生在你将一个大的数据类型转换为小的数据类型的时候,或者你把两个不同的数据类型相互转换的时候。
ClickHouse
1. 简单介绍一下ClickHouse
ClickHouse的全称是Click Stream,Data WareHouse,简称ClickHouse。
ClickHouse 是近年来备受关注的开源列式数据库管理系统,主要用于数据分析 (OLAP)领域。通过向量化执行以及对CPU底层指令集(SIMD)的使用,它可以对海量数据进行并行处理,从而加快数据的处理速度。
ClickHouse从 OLAP 场景需求出发,定制开发了一套全新的高效列式存储引擎,并且实现了数据有序存储、主键索引、稀疏索引、数据 Sharding、数据 Partitioning、TTL、主备复制等丰富功能。
2. ClickHouse 有哪些应用场景
- 绝大多数请求都是用于读访问的;
- 数据需要以大批次(大于 1000 行)进行更新,而不是单行更新;
- 数据只是添加到数据库,没有必要修改;
- 读取数据时,会从数据库中提取出大量的行,但只用到一小部分列;
- 表很“宽”,即表中包含大量的列;
- 查询频率相对较低(通常每台服务器每秒查询数百次或更少);
- 对于简单查询,允许大约 50 毫秒的延迟;
- 列的值是比较小的数值和短字符串(例如,每个 URL只有 60 个字节);
- 在处理单个查询时需要高吞吐量(每台服务器每秒高达数十亿行);
- 不需要事务;
- 数据一致性要求较低;
- 每次查询中只会查询一个大表。除了一个大表,其余都是小表;
- 查询结果显著小于数据源。即数据有过滤或聚合。返回结果不超过单个服务器内存。
3. ClickHouse具有哪些特点
- 支持完备的SQL操作
- 列式存储与数据压缩
- 向量化执行引擎
- 关系型模型(与传统数据库类似)
- 丰富的表引擎
- 并行处理
- 在线查询
- 数据分片
4. ClickHouse有哪些劣势
-
不支持真正的
delete/update
操作,不支持transactions
(事物) -
不支持二级索引
-
不支持高并发查询,官方建议
100 QPS
ClickHouse是并行计算,单个查询就可以跑满多个CPU核心,而不像MySQL单个查询单线程执行。
-
需要批量写入,频繁的单条写入会带来写入问题
ClickHouse存储结构有点类LSM,每次的insert基本都会生成一个文件目录,后台线程Merge目录文件,如果频繁写入,
后台线程就会Merge不过来,产生Too many parts
异常。建议每秒不超过一次写入,并且是Batch写入。 -
有限的SQL语法支持,JOIN语法也比较另类,暂时不支持窗口函数
5. ClickHouse的架构

-
ClickHouse 采用典型的分组式的分布式架构,包括:
-
Shard:集群内划分为多个分片或分组(Shard 0 … Shard N),通过 Shard
的线性扩展能力,支持海量数据的分布式存储计算。
-
Node:每个 Shard 内包含一定数量的节点(Node,即进程),同一 Shard 内的节点互为副本,保障数据可靠。
ClickHouse 中副本数可按需建设,且逻辑上不同 Shard 内的副本数可不同。
-
ZooKeeper Service:集群所有节点对等,节点间通过 ZooKeeper 服务进行分布式协调
-
6. ClickHouse为何如此之快
-
将硬件性能发挥到极致
基于将硬件功效最大化的目的,ClickHouse会在内存中进行GROUP BY,并且使用HashTable装载数据。
-
算法方面精益求精
在ClickHouse的底层实现中,经常会面对一些重复的场景,例如字符串子串查询、数组排序、使用HashTable等。对于不同的场景会用不同的算法。
-
勇于尝鲜,不行就换
除了字符串之外,其余的场景也与它类似,ClickHouse会使用最合适、最快的算法。如果世面上出现了号称性能强大的新算法,ClickHouse团队会立即将其纳入并进行验证。如果效果不错,就保留使用;如果性能不尽人意,就将其抛弃。
-
特定场景,特殊优化
针对同一个场景的不同状况,选择使用不同的实现方式,尽可能将性能最大化。关于这一点,其实在前面介绍字符串查询时,针对不同场景选择不同算法的思路就有体现了。类似的例子还有很多,例如去重计数uniqCombined函数,会根据数据量的不同选择不同的算法:当数据量较小的时候,会选择Array保存;当数据量中等的时候,会选择HashSet;而当数据量很大的时候,则使用HyperLogLog算法。
对于数据结构比较清晰的场景,会通过代码生成技术实现循环展开,以减少循环次数。接着就是大家熟知的大杀器—向量化执行了。SIMD被广泛地应用于文本转换、数据过滤、数据解压和JSON转换等场景。相较于单纯地使用CPU,利用寄存器暴力优化也算是一种降维打击了。 -
持续测试,持续改进
如果只是单纯地在上述细节上下功夫,还不足以构建出如此强大的ClickHouse,还需要拥有一个能够持续验证、持续改进的机制。由于Yandex的天然优势,ClickHouse经常会使用真实的数据进行测试,这一点很好地保证了测试场景的真实性。与此同时,ClickHouse也是我见过的发版速度最快的开源软件了,差不多每个月都能发布一个版本。没有一个可靠的持续集成环境,这一点是做不到的。正因为拥有这样的发版频率,ClickHouse才能够快速迭代、快速改进。
-
行存储和列存储
分析场景中,我们一般会读大量的行而取少量的列,在列式存储结构下,我们只需要取对应的列数据就可以,不参与计算的列完全不会被扫描到,这会极大的降低磁盘 IO 的消耗。
-
数据压缩
基于列式存储的结构,同一列中的数据属于同一类型,压缩效果会更加显著。列存储往有着高达十倍甚至更高的压缩比,节省了大量的存储空间,降低了存储成本。
-
向量化执行引擎
SIMD(Single Instruction Multiple Data)即单条指令操作多条数据,它是通过数据并行以提高性能的一种方式,可以简单理解为在寄存器层面对程序中的数据做并行处理,Clickhouse 在能够提升计算效率的地方大量使用了 SIMD,通过使用 SIMD,基本上能带来几倍的性能提升,像阿里云的 PolarDB-X 也引入了向量化执行引擎,为表达式计算带来了几十倍的性能提升。
-
多线程与分布式
分布式领域存在一条定律,计算移动比数据移动更加划算,这也是其核心所在,将数据的计算直接发放到数据所在的服务器,多机并行处理,再把最终的结果汇集在一起;另外 Clickhouse 也通过线程级别并行的方式为效率进一步提速,极致去利用服务器的资源。
-
多样化的表引擎
ClickHouse 提供了大量的数据引擎,分为数据库引擎、表引擎
MergeTree 作为家族系列最基础的表引擎,主要有以下特点:- 存储的数据按照主键排序,允许创建稀疏索引,从而加快数据查询速度;
- 支持分区,可以通过 PRIMARY KEY语句指定分区字段
- 支持数据副本
- 支持数据采样
ClickHouse-23.2.1.2537 单机安装部署文档(RPM版安装)
1、下载rpm文件
rpm和gz包的区别:
# 文件类型:
rpm是一种二进制文件格式,通常用于RedHat、CentOS等基于RPM包管理器的Linux发行版;而gz则是一种压缩文件格式,通常用于源代码或二进制可执行程序的发布。
# 安装方式:
rpm可以通过命令行工具(例如yum或rpm命令)进行安装、升级和卸载,也可以使用图形界面工具进行操作;而gz需要先解压缩,然后根据不同的程序安装方式进行安装和配置。
# 依赖性检测:
rpm可以自动检测并处理安装所需的运行库和依赖项,避免了手动安装依赖项的烦恼;而gz则需要手动检查和安装所需的依赖项。
# 版本控制:
rpm可以对软件版本进行管理,并支持安装多个版本的软件,方便用户进行升级和回滚;而gz则需要手动管理不同版本的软件包,较为麻烦。
下载地址:https://packages.clickhouse.com/rpm/stable/
2、上传rmp文件到Linux中
我的目录是 /usr/local/soft/clickhouse-install 其中clickhouse-install是自己创建的
3、开始安装
1、进入目录:
cd /usr/local/soft/clickhouse-install
2、使用rpm命令安装
sudo rpm -ivh *.rpm
注意:安装过程需要输入密码,密码不要复杂,123456即可
Password for default user is saved in file /etc/clickhouse-server/users.d/default-password.xml.
3、启动服务
systemctl start clickhouse-server.service
4、状态查看
systemctl status clickhouse-server.service
5、停止服务
systemctl stop clickhouse-server.service
6、重启服务
systemctl restart clickhouse-server.service
安装时输入密码截图,输入的时候不会显示
运行成功状态截图
4、远程工具连接
首先修改clickhouse配置文件让其可以被外界访问
1、打开clickhouse配置文件
vim /etc/clickhouse-server/config.xml
2、搜索并放开下面配置的注释
<listen_host>0.0.0.0</listen_host>
3、保存即可
:wq!
4、重启
systemctl restart clickhouse-server
查看端口号被哪个服务所占用
netstat -lnpt | grep 9001
使用命令行进入clickhouse客户端
clickhouse-client --port 9001 --password 123456
打开DBeaver,新建连接,选择ClickHouse
点击“下一步”,设置JDBC连接,配置主机,用户名和密码
点击“编辑驱动设置”,配置ClickHouse驱动包,下载完成后,点击“确定”
测试连接
然后就可以操作啦
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?