2024年8月

摘要:
现在商用优化器大多都是基于统计信息进行查询代价评估,因此统计信息是否实时且准确对查询影响很大,特别是分布式数据库场景。本文详细介绍GaussDB(DWS)如何实现了一种轻量、实时、准确的统计信息自动收集方案。

本文分享自华为云社区
《【最佳实践】GaussDB(DWS) 统计信息自动收集方案》
,作者: leapdb。

一、统计信息收集痛点

  1. 何时做analyze,多做空耗系统资源,少做统计信息不及时。
  2. 多个数据源并发加工一张表,手动analyze不能并发。
  3. 数据修改后立即查询,统计信息实时性要求高。
  4. 需要关心每张表的数据变化和治理,消耗大量人力。

二、基本功能介绍

三、自动收集方案

GaussDB(DWS) 支持统计信息自动收集功能,主要解决统计信息收集不及时和不准确的问题。

手动采样:用户在作业中,手动发起的显示analyze。

轮询采样:autovacuum后台线程,轮询发起的analyze。

动态采样:查询时,优化器触发的runtime analyze。

前台动态采样:负责统计信息实时准确,信息放内存(有淘汰机制),一级锁(像查询一样轻量)。

autoanalyze=on;
autoanalyze_mode
='light';

后台轮询采样:负责统计信息的持久化,写系统表(四级锁),不要求特别及时。

autovacuum_mode=mix或analyze;---以前只有“后台轮询采样”,都由后台autovacuum线程控制做vacuum或analyze。---后来开发“前台动态采样”,叫autoanalyze。--- 请注意二者的区别。

二者都需要开启。

替代场景

统计信息基于收集时表数据生成,数据变化较多后可能失效。自动触发也是基于阈值(50+表大小*10%)。

总结:

  1. 小表变化<10%且数据特征变化明显,需要“调低阈值自动收集”。
  2. 调整过采样大小且实时性要求高的场景,需要“主动收集统计信息”。
  3. 外表和冷热表因访问性能问题,不支持自动,需要“主动收集统计信息”。

四、如何保证及时触发

【触发条件】“无统计信息” or “表的修改量超过一定阈值(默认“50 + 表大小 * 10%”)”

【触发场景】含stream计划的SQL都可触发动态采样,包括select和带条件的delete, update。

【修改计数】

1. 哪些修改行为会被记录?

DML: Insert, Update, Delete, Copy, Merge,会累加修改计数。

DDL: truncate table,truncate/exchange/drop partition, alter column type, alter distribute,由于CN无法获取DN修改计数,所以直接记录一个超大修改计数。

2. 跨CN查询场景,如何确保修改计数全局一致?

异步广播:autovacuum后台线程轮询检查时,向所有CN广播全局修改计数。修改计数达2/3时广播一次,此后每增10%再广播一次。

实时广播:单SQL修改超过tuple_change_sync_threshold(默认1W)条时,直接实时广播修改计数到其它CN。

总结:“修改计数记录”和“修改计数广播”,覆盖都比较全面,能够保证查询及时触发动态采样。

五、最佳实践

GaussDB(DWS) analyze使用指南8.1.3及以下版本

GaussDB(DWS) analyze使用指南8.2.0及以上版本

1.事务块中手动analyze堵塞其它业务

【业务场景】

BEGIN;
ANALYZE t_ucuser;
INSERT INTO t_user_name(project_id, account_id, name_id, uid, etl_time)
with t1 AS (
selectproject_id, account_id, name_idfromt_user_name
WHERE uid
is null or uid = '')selecta.project_id,a.account_id,a.name_id, b.user_name AS uid, CURRENT_TIMESTAMP AS etl_timefrom t1 a join t_ucuser b ON a.project_id = b.project_id AND a.account_id =b.account_id
ON CONFLICT(project_id,account_id,name_id) DO UPDATE
SET project_id
=excluded.project_id, account_id=excluded.account_id, name_id=excluded.name_id, uid=excluded.uid, etl_time=excluded.etl_time;
END;

【问题根因】

a. 某数据湖用户,多个数据源按照不同的分区进行数据导入加工。

b. 事务块中有手动analyze,且事务块中后面的查询长时间执行不完。

c. 因analyze对表加四级锁长时间不能释放,导致其它相关表上的业务等锁超时报错。

【解决方案】开启light动态采样,去掉事务块中的手动analyze。

2. 多数据源并发加工同一张表的不同分区

【业务场景】

为了保证用户查询表总有数据,需要把加工过程放到一个事务里面。堵塞其它人的动态采样。

begin;
alter table tab_partition truncate partition P2023_03;
insert into tab_partition
select * fromt1;
end;

【问题根因】alter table truncate parition对分区加8级锁,事务过程中长时间持锁。

【解决方案】使用exchange partition

CREATE TABLE IF NOT EXISTS tab_tmp1(like tab_partition INCLUDING DROPCOLUMNS INCLUDING DISTRIBUTION INCLUDING STORAGE INCLUDING RELOPTIONS);
INSERT INTO tab_tmp1 SELECT
*FROM t1;
ALTER TABLE tab_partition exchange partition (P2023_03) WITH TABLE tab_tmp1;

3.多表并发反序analyze导致统计信息收集失败

【业务场景】

a. 某银行客户,多个表进行批处理数据加工,开启了normal类型动态采样。

b. 查询A先对t1表触发动态采样,再对t2表触发动态采样。

c. 查询B先对t2表触发动态采样,再对t1表触发动态采样。

d. 触发动态采样的顺序不一致,互相申请四级锁导致申锁超时,统计信息未收集。

【问题根因】多人同时按不同顺序analyze多表导致死锁。

【解决方案】开启light动态采样,仅加一级锁不再有四级锁冲突。

4.刚导入的数据不在统计信息中导致查询计划差

【业务场景】

a. 某财经用户,按照月度视为会计期,月初时导入少量数据,然后马上查询。

b. 触发了动态采样,但采集不到最新会计期的少量数据。

【问题根因】新插入数据占比小,及时触发了动态采样但采集不到,导致估算偏差大。

【解决方案】

a. 开启统计信息推算enable_extrapolation_stats功能,根据上一个会计期的统计信息推算当前会计期数据特征。

b. 不提高采样大小,利用历史信息增强统计信息准确性。

5.随机函数质量差导致数据特征统计不准

【业务场景】

a. 某银行客户,按月度条件进行关联查询

b. 多次analyze,最多数据月份在MCV中占比从13%~30%大幅波动

c. 详细输出样本点位置和采样随机数发现,随机数(小数点后6位)生成重复度高导致采样扎堆儿严重。

【问题根因】采样随机数不够随机,样本采集不均匀导致MCV数据特征统计偏差。

【解决方案】

a. 每次传入随机种子再生成随机数,提高随机性和并发能力。控制参数random_function_version。

b. 不提高采样大小,提升随机数质量增强统计信息准确性。

6.样本分布不均匀导致数据特征统计不准

【业务场景】

a. tpc-h的lineitem表l_orderkey列,数据每4~8条批量重复。即同一个订单购买多个商品。

b. 传统采样算法由于采样不均匀,采集到的重复数据稍多,导致采集的distinct值偏低。

【问题根因】数据特征分布不均匀,采样无法抓准数据特征,distinct值高的场景统计出的distinct值偏低。

【解决方案】

a. 使用自研的优化蓄水池采样算法,控制参数analyze_sample_mode=2,让采样更加均匀,以提升统计信息准确性。

b. 如果上述方法没有达到预期效果,可以手动修改distinct值。

select APPROX_COUNT_DISTINCT(l_orderkey) from lineitem; --近似计算distinct值
alter table lineitem alter l_orderkey
set (n_distinct=10000); --手动设置distinct值,然后再analyze即可。

点击关注,第一时间了解华为云新鲜技术~

各位道友大家好呀!

想必道友们或多或少都听说过MySQL的binlog的作用,它记录了数据库整个的生命周期,可用于恢复数据或者从库同步数据。

那么如果发生了数据库误删,具体该怎样恢复数据呢?

下面就以一个例子来给道友们演示一下,让我们开始吧!do it!

数据备份

首先,数据库要定时进行备份,因为如果需要恢复数据,可以缩小binlog执行范围

mysqldump -uroot -p --master-data=2 --single-transaction -A > /www/server/data/fanrencode-back.sql

参数master-data为2可以记录更多的细节,比如位置信息,方便binlog
--start-position
定位

binlog恢复数据

模拟误删操作

首先,新增一条测试数据,然后对库进行备份


然后我们又新增了数据,但是不小心把表给清空了

那么我们该怎么恢复数据呢?

有条件的话先在测试库里执行,没问题再在生产执行。

恢复数据

1.执行备份的文件

可以看到数据已经恢复成mysqldump备份时的数据了

2.执行binlog

查看mysqldump备份的文件的结束位置,如图可知是1969

注意这个MASTER_LOG_POS在文件的最上面,用PgUp翻到最上面即可。

然后查询mysql-bin.000011这个binlog文件,直接vim查看的话会乱码,所以我们把文件以明文的形式(-vv)输出到另一个test.binlog文件

/www/server/mysql/bin/mysqlbinlog mysql-bin.000011 -vv > test.binlog

如果提示unknown variable 'default-character-set=utf8'.错误,则需要在mysqlbinlog 后面加上--no-defaults参数

然后再vim查看就不乱码了,由图可知truncate操作的位置在902120

然后执行恢复操作

/www/server/mysql/bin/mysqlbinlog  --start-position=1969 --stop-position=902120 /www/server/data/mysql-bin.000011 | mysql -uroot -p

这样就完成了数据的恢复

当当,是不是还挺简单的,各位道友实操起来若有参差欢迎给小道留言~

动态分支预测是一种通过记录和分析程序运行时分支行为的历史信息来预测未来分支的机制。这种技术旨在提高处理器流水线的效率,减少分支指令引起的流水线停顿。你提到的通过查找指令地址判断分支行为的方法,就是一种动态分支预测的实现。

体现动态分支预测的几个关键点

  1. 历史信息记录


    • 记录分支行为
      :动态分支预测器会记录每个分支指令的历史行为,例如分支是否发生,以及发生的频率。这些信息通常存储在名为分支历史表(BHT, Branch History Table)或者模式历史表(PHT, Pattern History Table)中。
    • 更新预测信息
      :每次分支指令执行后,预测器会更新这些历史信息,使得未来的预测能够更准确。
  2. 预测算法


    • 单一位预测器
      :最简单的动态分支预测器使用单一位来记录分支是否发生。如果上一次分支发生,则预测下一次也会发生;如果上一次没有发生,则预测下一次也不会发生。
    • 两位饱和计数器
      :一种更复杂的预测器使用两位状态机,能够更好地应对分支行为的变化。两位计数器有四种状态,通过更新状态来预测分支的发生与否。
    • 全局分支预测
      :利用全局分支历史来进行预测,可以考虑整个程序的分支模式。
    • 局部分支预测
      :利用局部分支历史,只考虑特定分支指令的历史行为。
  3. 动态调整


    • 动态分支预测器不断地根据运行时的分支行为调整其预测模型。每次分支指令执行时,预测器都会比较预测结果与实际结果,并根据实际情况调整其内部状态,以提高未来预测的准确性。
  4. 指令地址关联


    • 通过指令地址来索引和管理分支行为的历史信息,这是动态分支预测的核心之一。预测器会使用分支指令的地址来查找其对应的历史记录,并基于这些记录进行预测。
    • 如果预测上一次分支发生,处理器会预取上次分支跳转到的指令地址,从而减少可能的流水线停顿。

示例

假设有一个分支指令
B
,其地址为
0x100
。动态分支预测器会记录
B
的执行历史,并在预测时使用这些记录:

  1. 初始状态

    B
    指令的历史信息未记录或预测器初始为不跳转。
  2. 首次执行
    B


    • 实际执行
      B
      跳转到
      0x200
    • 更新记录:
      0x100
      的分支历史信息为跳转。
  3. 再次遇到
    B


    • 预测器查找
      0x100
      的历史记录,发现上次跳转,预测这次也会跳转。
    • 处理器预取
      0x200
      的指令。
  4. 第三次执行
    B


    • 预测器继续查找
      0x100
      的历史记录,根据前两次的行为继续预测跳转或不跳转。

结论

通过查找指令地址判断分支行为,并基于上次执行的结果来预测下一次执行时的分支行为,这种方法的动态性体现在它能够根据程序运行时的实际分支行为不断调整和优化预测模型,从而提高预测的准确性和处理器流水线的效率。这正是动态分支预测技术的核心特征。

在没有分支指令的情况下,程序确实会顺序执行,程序计数器(PC)会依次指向下一个指令地址。这意味着指令地址通常是连续分配的,但需要注意以下几点:

连续分配的原因

  1. 编译器行为


    • 编译器将源代码编译成机器码,并将这些机器码指令放入可执行文件中。在没有分支指令或其他控制流改变指令的情况下,编译器会按顺序将指令放入内存,这些指令的地址是连续的。
  2. 内存模型


    • 计算机的内存模型是线性的,内存地址是连续的空间。因此,当程序加载到内存时,指令也按顺序加载到连续的内存地址中。

例子

假设有一段简单的代码:

int main() {
    int a = 5;
    int b = 10;
    int c = a + b;
    return c;
}

编译器将其编译成汇编代码(简化示例):

0x1000:  MOV R1, #5       ; 将5加载到R1寄存器
0x1004:  MOV R2, #10      ; 将10加载到R2寄存器
0x1008:  ADD R3, R1, R2   ; R3 = R1 + R2
0x100C:  MOV R0, R3       ; 将结果移动到R0寄存器
0x1010:  BX LR            ; 返回

在这个示例中:

  • 每条指令占用一个特定的内存地址,并且这些地址是连续的:
    0x1000
    ,
    0x1004
    ,
    0x1008
    ,
    0x100C
    ,
    0x1010
  • 没有分支指令的情况下,PC会从
    0x1000
    开始,依次指向下一个连续的指令地址。

需要注意的地方

  1. 程序的整体结构


    • 尽管大多数指令地址是连续的,但整个程序可能包含多个代码段(如函数、子程序)。这些代码段之间可能会有分隔,并不总是连续的。
  2. 内存对齐


    • 有些处理器架构要求指令地址按照特定对齐方式(如4字节对齐)。这意味着即使指令长度不同,地址仍可能按照对齐要求分配。
  3. 优化


    • 编译器优化可能会导致某些指令被重排序或插入填充指令(如NOP),这些情况也可能影响指令的连续分配。

结论

在没有分支指令的情况下,指令地址通常是按顺序分配的,这是因为编译器和内存模型的工作方式。然而,整个程序的布局、内存对齐和编译器优化等因素都可能影响指令地址的分配方式。总体来说,程序在没有分支的情况下会顺序执行,并且指令地址通常是连续的。

在动态分支预测中,使用一位预测位的方法是一种最基本的预测机制,称为单比特分支预测(One-bit Branch Prediction)。该方法简单且易于实现,但确实存在一些性能上的缺陷,如你所提到的:即使一个分支几乎总是发生,它仍会在某些情况下导致多次误预测。

一位预测位的工作原理

一位预测位的预测方法依赖于一个单比特来记录分支上一次是否被采取(Taken)或未被采取(Not Taken):

  • 预测位 = 1
    :预测分支将被采取。
  • 预测位 = 0
    :预测分支将不被采取。

性能上的缺陷

让我们分析一下你提到的性能缺陷:

即使一个分支几乎总是发生,但仍会发生两次误预测,而不是分支不发生时的一次。

示例

假设我们有一个循环,其中的分支几乎总是被采取,但在最后一次迭代时不被采取:

for (int i = 0; i < 10; i++) {
    // Loop body
}

对于这个循环:

  • 前9次迭代中,分支是被采取的(继续循环)。
  • 在第10次迭代结束时,分支是不被采取的(退出循环)。

一位预测位的缺陷

  1. 初始状态
    :假设预测位初始为0(不被采取)。

  2. 第一次迭代


    • 预测:不被采取。
    • 实际:被采取(误预测)。
    • 更新预测位:1(被采取)。
  3. 第二到第九次迭代


    • 预测:被采取。
    • 实际:被采取(预测正确)。
    • 预测位保持1(被采取)。
  4. 第十次迭代


    • 预测:被采取。
    • 实际:不被采取(误预测)。
    • 更新预测位:0(不被采取)。
  5. 再次进入循环


    • 预测:不被采取。
    • 实际:被采取(误预测)。
    • 更新预测位:1(被采取)。

总结

  • 在前9次迭代中,预测位逐渐被更新为1,预测正确。
  • 在第10次迭代中,预测位导致一次误预测。
  • 在再次进入循环时,由于预测位刚被更新为0,又导致一次误预测。

因此,即使一个分支几乎总是发生,在使用一位预测位的方法下,仍会发生两次误预测:一次是在分支实际不被采取时,另一次是在分支再次被采取时。这个缺陷导致预测效率低下。

解决方案

为了解决这一缺陷,可以采用更复杂的分支预测方法,例如两位饱和计数器(Two-bit Saturating Counter):

  • 使用两位表示四种状态:强预测不被采取、弱预测不被采取、弱预测被采取、强预测被采取。
  • 这种方法可以减少误预测次数,因为只有在连续两次预测错误后,预测方向才会改变。

总结

一位预测位的方法简单但存在性能缺陷,特别是在分支几乎总是发生的情况下会导致两次误预测。更复杂的预测机制(如两位饱和计数器)可以通过引入更多状态来减少误预测次数,提高分支预测的准确性。

两位饱和计数器的动态分支预测与机器学习中的一些知识点可以结合,特别是在状态转换和概率预测的概念上。以下是一些相关的知识点:

1.
有限状态机(Finite State Machine, FSM)

两位饱和计数器本质上是一种简单的有限状态机,具有4个状态:

  • 强不采取(Strongly Not Taken)
  • 弱不采取(Weakly Not Taken)
  • 弱采取(Weakly Taken)
  • 强采取(Strongly Taken)

这种状态机在机器学习中可以类比为状态空间模型,如马尔可夫链。

2.
马尔可夫链(Markov Chain)

两位饱和计数器的状态转换可以被视为一个马尔可夫链,具有以下特征:

  • 状态
    :4种状态(SNT, WNT, WT, ST)
  • 转移
    :基于当前分支的结果(Taken或Not Taken)

马尔可夫链是机器学习中用来描述系统状态和状态之间转移的工具,常用于预测未来状态。

3.
概率预测

虽然两位饱和计数器是一个确定性模型,但它的设计理念与概率预测相似:

  • 强采取和强不采取状态
    :类似于高置信度的预测。
  • 弱采取和弱不采取状态
    :类似于低置信度的预测。

在机器学习中,概率预测模型如朴素贝叶斯、隐马尔可夫模型(Hidden Markov Model, HMM)等,会根据历史数据进行状态预测,这与两位饱和计数器根据历史分支行为进行预测有相似之处。

4.
强化学习(Reinforcement Learning, RL)

强化学习中的Q-Learning算法与两位饱和计数器有一些相似之处:

  • Q-Learning
    :基于当前状态和采取的动作,更新Q值,逐渐学习最优策略。
  • 两位饱和计数器
    :基于当前分支结果(Taken或Not Taken),更新状态,逐渐优化预测策略。

在强化学习中,Q值更新类似于两位饱和计数器中的状态更新,通过不断调整,达到更准确的预测。

5.
记忆机制(Memory Mechanisms)

两位饱和计数器在一定程度上可以类比为具有记忆的神经网络,如长短期记忆网络(LSTM)和门控循环单元(GRU):

  • 记忆
    :保存前几次的分支结果,用于指导当前预测。
  • LSTM/GRU
    :通过门机制保留重要的历史信息,抑制不重要的信息,以更好地预测未来。

结合示例

假设我们用机器学习的方法来模拟分支预测,可以使用马尔可夫链或简单的强化学习算法来进行预测:

import numpy as np

class TwoBitPredictor:
    def __init__(self):
        # States: 00 = SNT, 01 = WNT, 10 = WT, 11 = ST
        self.state = 0b10  # Start with weakly taken (WT)
    
    def predict(self):
        # Predict taken if in states 10 (WT) or 11 (ST)
        return self.state >> 1
    
    def update(self, taken):
        # Update state based on the actual branch result
        if taken:
            if self.state < 0b11:
                self.state += 1
        else:
            if self.state > 0b00:
                self.state -= 1

# Simulate the predictor
predictor = TwoBitPredictor()
branches = [True, True, False, True, True, False, True, False]

for branch in branches:
    prediction = predictor.predict()
    print(f"Prediction: {'Taken' if prediction else 'Not Taken'}, Actual: {'Taken' if branch else 'Not Taken'}")
    predictor.update(branch)

这种简单的模拟展示了如何用一个两位饱和计数器来进行动态分支预测。通过这种方式,可以将机器学习中的一些基本概念与动态分支预测结合起来,帮助理解和改进预测算法。

分支预测器的主要作用是提高计算机处理器的性能。具体来说,分支预测器通过预测程序中分支指令(如条件跳转、循环和函数调用等)的执行路径,减少或消除分支指令导致的流水线停顿和延迟。以下是分支预测器的详细作用:

1.
提高指令流水线的效率

现代处理器使用流水线技术来提高指令执行的并行度和效率。然而,分支指令(如条件跳转和循环)会中断指令流水线的顺序执行。分支预测器通过预测分支的方向,使得处理器可以继续加载和执行后续指令,而不必等待分支指令的结果,从而减少流水线停顿,提高流水线的利用率。

2.
减少指令执行的等待时间

当处理器遇到分支指令时,如果无法预测分支的结果,处理器将不得不等待分支指令的执行结果。这会导致处理器进入等待状态,降低执行效率。分支预测器通过预测分支的结果,允许处理器继续执行预测路径上的指令,减少等待时间,提高指令执行速度。

3.
提高指令级并行性(Instruction-Level Parallelism, ILP)

分支预测器使得处理器能够在预测的分支路径上提前加载和执行指令,从而提高指令级并行性。通过增加指令的并行执行,处理器可以更有效地利用其资源,提高整体性能。

4.
减少分支错预测带来的性能损失

虽然分支预测器不能保证每次预测都是正确的,但通过使用先进的预测算法(如两位饱和计数器、全局历史寄存器和混合预测器等),可以显著降低分支错预测的概率。当分支预测器做出正确预测时,可以避免因分支错预测而带来的流水线清空和重新加载的开销,从而减少性能损失。

5.
提高处理器的吞吐量

分支预测器的准确预测可以使处理器保持高效的指令吞吐量,即每个时钟周期内处理的指令数量。通过减少分支指令引起的停顿和延迟,处理器能够更连续和快速地执行指令,提高整体吞吐量。

示例:两位饱和计数器的分支预测器

假设一个简单的两位饱和计数器分支预测器。该预测器有四个状态:强不采取(00)、弱不采取(01)、弱采取(10)和强采取(11)。预测器根据分支指令的历史执行结果进行状态转移,并据此预测分支的方向。其工作原理如下:

  1. 初始状态
    :假设初始状态为弱采取(10)。
  2. 预测分支
    :根据当前状态进行预测。如果状态为10或11,则预测分支将被采取;如果状态为00或01,则预测分支不被采取。
  3. 执行分支
    :处理器执行分支指令,并根据实际结果更新状态。
  4. 状态更新

    • 如果预测正确,状态保持不变或向更强的方向转移。
    • 如果预测错误,状态向相反方向转移。

以下是一个示例代码,展示了两位饱和计数器的基本工作流程:

#include <iostream>
#include <vector>

class TwoBitPredictor {
public:
    enum State { SNT, WNT, WT, ST };

    TwoBitPredictor() : state(WT) {}

    bool predict() const {
        return state == WT || state == ST;
    }

    void update(bool taken) {
        if (taken) {
            if (state != ST) state = static_cast<State>(state + 1);
        } else {
            if (state != SNT) state = static_cast<State>(state - 1);
        }
    }

private:
    State state;
};

int main() {
    TwoBitPredictor predictor;
    std::vector<bool> branches = {true, true, false, true, true, false, true, false};

    for (bool branch : branches) {
        bool prediction = predictor.predict();
        std::cout << "Prediction: " << (prediction ? "Taken" : "Not Taken")
                  << ", Actual: " << (branch ? "Taken" : "Not Taken") << std::endl;
        predictor.update(branch);
    }

    return 0;
}

在这个示例中,
TwoBitPredictor
类实现了一个简单的两位饱和计数器预测器。通过预测和更新状态,该预测器能够根据历史分支结果进行动态分支预测,提高处理器的性能。

PostgreSQL数据库介绍

PostgreSQL是一个功能强大的开源对象关系型数据库管理系统(RDBMS)。最初于1986年在加州大学伯克利分校的POSTGRES项目中诞生,PostgreSQL以其稳定性、灵活性和扩展性而著称。它支持丰富的数据类型、复杂的查询、事务完整性、多版本并发控制(MVCC)、存储过程等高级功能。

PostgreSQL完全遵循SQL标准,支持ACID属性(原子性、一致性、隔离性、持久性),适用于高并发和数据量大的应用场景。此外,它具有广泛的可扩展性,允许用户定义自己的数据类型、索引方法、函数、操作符等。PostgreSQL的强大社区不断提供支持和扩展,使其在不断发展的数据库技术中保持竞争力。被广泛应用于数据分析、金融服务、Web开发等领域。

官网地址:
https://www.postgresql.org

GitHub地址:
https://github.com/postgres/postgres

image-20240807140547880

PostgreSQL:The World's Most Advanced Open Source Relational Database.

PosegreSQL:世界上最先进的开源关系型数据库。

Entity Framework Core介绍

EF Core是专为.NET设计的现代化对象数据库映射器。它支持LINQ查询,变更跟踪,更新以及模式迁移。EF Core支持与SQL Server,Azure SQL数据库,SQLite,Azure Cosmos DB,MySQL,PostgreSQL以及通过提供程序插件接口的集成其他数据库。

通过EF Core,开发者能够更高效地开发数据驱动的应用程序,适用于Web应用、桌面应用、微服务等多种应用场景。其不断更新的版本和活跃的社区支持,使其成为.NET开发者首选的ORM框架之一。

GitHub地址:
https://github.com/dotnet/efcore

文档地址:
https://learn.microsoft.com/zh-cn/ef/core

image-20240807141748135

实践

IDE:Visual Studio 2022

.NET版本:.NET 8

新建一个Web Api项目。

本次实践需要用到3个包:

image-20240807142146162

image-20240807142204223

image-20240807142227059

第一个就是EF Core。

第二个
Microsoft.EntityFrameworkCore.Tools
是一个为 Entity Framework Core 提供命令行工具支持的 NuGet 包。这个工具包主要用于数据库迁移的创建、更新和管理,生成数据库上下文类和实体类等。通过使用该工具,开发者可以从命令行或包管理器控制台执行操作,如创建新迁移、应用迁移、生成数据库脚本等。这有助于在开发过程中保持数据库模式与代码模型的一致性。

第三个
Npgsql.EntityFrameworkCore.PostgreSQL
是一个用于将 Entity Framework Core(EF Core)与 PostgreSQL 数据库结合使用的提供程序包。它为 EF Core 提供了对 PostgreSQL 数据库的支持,使开发者能够使用 EF Core 的功能来处理 PostgreSQL 数据库中的数据。通过这个包,开发者可以使用 LINQ 查询、自动迁移、模型验证等 EF Core 特性,并且可以利用 PostgreSQL 特有的功能,如 JSONB 数据类型、全文搜索、数组等。

GitHub地址:
https://github.com/npgsql/efcore.pg

首先在项目根目录创建一个名为Data的文件夹,创建一个AppDbContext类。

image-20240807143755865

先大体上总览一下这个类:

image-20240807143914379

首先它继承自DbContext类。

在 Entity Framework Core(EF Core)中,
DbContext
类是核心组件之一,负责管理与数据库的所有交互。它充当应用程序与数据库之间的桥梁,提供了查询数据库、保存数据以及配置模型的功能。

主要功能和角色

  1. 数据访问和查询

    DbContext
    提供了一组方法和属性,允许开发者使用 LINQ 查询数据库。通过访问
    DbSet<TEntity>
    属性,可以对特定实体类型执行查询操作。
  2. 对象追踪和变更检测

    DbContext
    追踪从数据库中检索到的实体对象的状态。当对象的状态发生变化时(如被修改、添加或删除),
    DbContext
    负责记录这些变化,并在调用
    SaveChanges()
    方法时,将这些变化应用到数据库中。
  3. 事务管理

    DbContext
    支持事务的管理,通过
    SaveChanges()
    方法确保数据库操作的原子性,即所有的操作要么全部成功,要么全部回滚。
  4. 模型配置

    DbContext
    允许通过覆盖
    OnModelCreating
    方法来配置实体模型与数据库表之间的映射关系。这包括设置主键、索引、外键关系、约束等。
  5. 生命周期管理

    DbContext
    是一个可配置的类,其生命周期管理可以根据需要进行配置。通常,它在请求或操作的范围内被创建和释放,以确保数据库连接的有效管理。

还有一个类型为IConfiguration的属性,它通过构造函数注入。
IConfiguration
是一个接口,主要用于获取应用程序的配置数据。它提供了一种标准化的方式来访问应用程序的配置信息,例如连接字符串、应用设置、外部服务的密钥等。
IConfiguration
允许开发者从各种来源(如 JSON 文件、环境变量、命令行参数等)读取配置,并将这些配置统一映射到一个结构化的对象中。

在Web Api中,我们一般把一些配置写在appsettings.json中。

image-20240807144847765

本示例的appsettings.json如下所示:

image-20240807145043754

存储了PostgreSQL的连接字符串。

在OnConfiguring方法中进行连接字符串的配置。

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 {
     optionsBuilder.UseNpgsql(Configuration.GetConnectionString("SKApiDatabase"));
 }

现在新建一个Student类:

 public class Student
 {
     public int Id { get; set; }
     public string? Name { get; set; }
     public string? Home { get; set; }
 }

在AppDbContext类中添加Student表:

 public DbSet<Student> Students { get; set; }

我们使用的是Code First。"Code First" 是 Entity Framework(包括 Entity Framework Core)中一种开发模式,它的主要思想是通过定义应用程序的领域模型(通常使用类)来创建和管理数据库架构。这种方法强调首先编写代码来定义数据模型,而数据库的表结构则是由这些数据模型自动生成和维护的。

  1. 领域模型的定义
    :在 Code First 模式中,开发者首先定义领域模型,即使用类和属性来表示数据库中的实体和关系。通过注解(数据注解属性)或流畅的API(Fluent API),开发者可以指定数据库表、列、主键、外键、索引等数据库结构信息。
  2. 数据库上下文类
    :定义一个继承自
    DbContext
    的类,它包含了对实体的
    DbSet<TEntity>
    属性。这些属性对应数据库中的表,
    DbContext
    类还负责管理数据访问和对象的生命周期。
  3. 迁移
    :EF Core 提供了迁移(Migration)工具,可以根据代码中的模型变更生成和应用数据库更新脚本。迁移可以帮助开发者将数据库架构从一个版本升级到另一个版本,同时保留数据。
  4. 自动生成数据库
    :在应用程序运行时,EF Core 可以根据定义的模型自动生成数据库架构。如果数据库已经存在,EF Core 可以检查和应用迁移来更新数据库。

优点

  1. 开发效率高
    :开发者可以专注于代码开发,而不需要直接编写 SQL 脚本来定义数据库结构。
  2. 易于维护
    :模型的变化可以通过代码和迁移工具方便地同步到数据库。
  3. 类型安全
    :代码和数据库模型是紧密绑定的,减少了由于不匹配导致的错误。

缺点

  1. 对复杂数据库的支持
    :对于已有的大型复杂数据库,Code First 可能不太合适,特别是在处理特定的数据库优化和配置时。
  2. 性能问题
    :自动生成的SQL可能没有手工优化的SQL高效。

总的来说,Code First 模式是一种简化开发和维护数据库架构的有效方法,特别适合于从头开始的新项目。

现在就需要用到Microsoft.EntityFrameworkCore.Tools了,打开程序包管理器控制台,输入

Add-Migration "备注信息"

image-20240807150340099

成功之后,会发现项目根目录多了个Migrations文件夹:

image-20240807150425459

Migrations
文件夹用于存放由迁移工具生成的迁移文件。这些文件记录了数据库架构的变更历史,使得开发者可以管理和应用这些变更,以保持数据库与代码模型之间的一致性。具体来说,
Migrations
文件夹及其内容的作用包括以下几个方面:

  1. 跟踪数据库架构的变更
    : 每当开发者对领域模型(实体类)或数据库上下文类进行修改(如添加新实体、修改属性类型等)并生成迁移时,EF Core 会在
    Migrations
    文件夹中创建一个新的迁移文件。这个文件包含了描述数据库如何从一个状态变更到另一个状态的指令。
  2. 生成和维护迁移脚本
    : 迁移文件中包含两个主要部分:
    Up
    方法和
    Down
    方法。
    Up
    方法定义了应用迁移时执行的操作,例如创建表、添加列等。而
    Down
    方法则定义了撤销这些变更的操作。通过这些方法,EF Core 可以生成适用于不同数据库提供程序的 SQL 脚本,以便在数据库中执行相应的变更。
  3. 应用迁移到数据库
    : 通过
    Update-Database
    命令或代码中的
    context.Database.Migrate()
    方法,EF Core 会依次应用
    Migrations
    文件夹中的迁移,更新数据库架构。这有助于在开发、测试和生产环境中保持数据库的一致性。
  4. 版本控制和协作
    : 迁移文件是普通的代码文件,可以纳入版本控制系统(如 Git)。这使得团队成员可以方便地跟踪数据库架构的变更,回顾和讨论不同版本之间的差异。

打开20240806093127_init类看看:

image-20240807150949302

就像上面第二点说的一样,包含两个主要部分:
Up
方法和
Down
方法。
Up
方法定义了应用迁移时执行的操作,

Down
方法则定义了撤销这些变更的操作。

打开程序包管理器控制台,输入

Update-Database

image-20240807151439774

现在打开pg Admin:

image-20240807151510445

pgAdmin 是一个用于管理 PostgreSQL 数据库的开源图形化用户界面工具。它提供了一个友好的界面,帮助用户执行数据库管理任务、编写和执行SQL查询、监控数据库状态等。pgAdmin 适用于各种平台,包括 Windows、macOS 和 Linux。

发现我们的数据库中成功生成了两张表:

image-20240807151824560

一张是在AppDbContext类中定义的Students表,一张是自动生成的用于记录迁移历史的__EFMigrationsHistory表。

以上就成功在Web Api中使用EF Core连接到PostgreSQL数据库了,接下来就可以开始愉快地CRUD了。

参考

1、
Setup PostgreSQL in .NET with Entity Framework (youtube.com)

在 AI 时代,DataOps 2.0 代表了一种全新的数据管理和操作模式,通过自动化数据管道、实时数据处理和跨团队协作,DataOps 2.0 能够加速数据分析和决策过程。它融合了人工智能和机器学习技术,使得数据的获取、清洗和分析更加高效,推动企业在快速变化的市场中保持竞争优势。

另一方面,在 AI 时代,调度编排技术正变得愈发重要,成为高效资源管理和任务自动化的核心。通过智能算法和机器学习,调度编排能够实时分析系统负载、优化资源分配,并根据需求动态调整任务执行顺序。这不仅提高了系统的运行效率,还降低了人力干预的需求,提升了响应速度和灵活性。

Bessemer Venture Partners 在基础设施投资方面有着悠久的历史。经过这家公司的长期观察,他们发现在 AI 时代,为 AI 量身定制的新型基础设施范式正在兴起,以增强 AI 时代下一波企业数据软件的发展。其中,DataOps 2.0 和调度编排技术和产业的发展也成为焦点。

以下为Bessemer Venture Partners 关于 AI 时代下新型基础设施的发展状况的观察和预测,仅供参考:


目录:

一、AI 革命正在催生数据堆栈的演变

二、为 AI 量身打造的新兴基础设施堆栈

1、在扩展、创新模型架构和专用基础模型方面的创新

2、模型部署和推理的创新

3、前沿模型训练和开发技术

4、AI 时代的 DataOps 2.0

5、下一代可观测性

6、编排

三、AI 基础设施业务存在巨大机遇

一、AI 革命正在催生数据堆栈的演变

近年来,机器学习取得显著进展——自 2017 年突破性论文《Attention is all you need》(
https://arxiv.org/abs/1706.03762)奠定了
transformer 深度学习架构的基础以来,我们迎来了 AI 研究的“寒武纪大爆发”,每天都有新的论文发表,并以惊人的速度不断积累。

AI 基础设施 arXiv 论文图表

AI 创新的这种结构性转变正在催化数据基础设施在许多方面的演变。

  • 首先,AI 正在推动现代数据堆栈的发展,现有的数据基础设施公司已经开始将 AI 功能整合到数据管理中的合成、检索和丰富等环节。

此外,认识到 AI 浪潮作为商业机会的战略重要性,一些现有公司甚至发布了全新的产品,以支持 AI 工作负载和 AI 优先用户。

例如,许多数据库公司现在将嵌入作为一种数据类型支持,要么作为新功能,要么作为独立产品提供。

  • 其次,数据和 AI 密不可分。数据的增长速度异常惊人,正在推动当前基础设施工具的极限。

特别是非结构化数据的生成量预计到 2030 年将飙升至 612 泽字节(一泽字节等于一万亿千兆字节或十亿兆字节。);

这一增长是由机器学习/AI 的热潮和生成模型在各类模式下产生的合成数据所驱动的;除了数据量外,数据类型和来源的复杂性和多样性也在不断增加。

公司正在通过开发新硬件来应对这些挑战,包括更强大的处理器(如 GPU、TPU)、更好的网络硬件以促进高效的数据传输,以及下一代存储设备。

  • 最后,基于最近在机器学习和硬件方面的进展,一波新的 AI 原生和 AI 嵌入的初创公司正在涌现——这些公司要么从一开始就利用 AI/ML,要么用其增强现有能力。

不幸的是,目前的许多数据基础设施和工具仍未针对 AI 用例进行优化。就像将方钉强行塞进圆孔一样,AI 工程师不得不在现有基础设施中创造变通的方法或技巧。

二、为 AI量身打造的新兴基础设施堆栈

随着近年来多个“why now”的推动因素积累,缺乏原生和专门设计的工具,促成了为 AI 原生和嵌入式 AI 公司构建的新 AI 基础设施堆栈。

我们正处于一场大规模技术变革的中期——这一新兴的 AI 基础设施堆栈内的创新正以前所未有的速度推进。

即使在我们编写这份路线图和发展我们的观点时,研究人员每天都在发布新的论文,使之前的观点变得过时。

瞬息万变的环境令人生畏,但尽管存在未知的变数,但初创企业的潜力和机会却是广阔的。

伴随着 AI 的革新,我们展开投资。随着每日发布的新前沿研究,有时感觉脚下的地面都在变化。我们不断将最新的发展纳入我们的理论。以下是我们感兴趣的几个主题:


1、在扩展、创新模型架构和专用基础模型方面的创新

模型层正成为 AI 基础设施堆栈中最具动态性和竞争激烈的层。

基础模型是新的“石油”,鉴于这一部分堆栈的战略重要性,随着越来越多的公司基于它们的启发式方法构建应用,这里的赢家可能在未来多年内定义下游应用的未来。

我们看到模型层的活动激增——从开源模型到小语言模型。大量的活动和资本集中在扩展基于 transformer 模型(如通过数据、模型并行、混合模态等)或试图推动这些模型在各种性能属性上的发展(如成本、延迟、部署、内存占用、上下文窗口等)。

例如,几支团队正在改进生成模型的构建块(primitives),如注意力机制和卷积机制,以创造更强大、更高效的 AI 技术。

由于模型训练需要大量的资金,许多这些需要风险投资资助。除了训练成本之外,还需要具备科研人才、工程化人才和专门资源来在这一层进行创新。

但是“attention is not all you need”——研究人员也在开发非 transformer 架构,并不断推动基础模型的可能性。

例如,状态空间模型(SSM),如 Mamba,以及各种递归架构,正在拓展基础模型的前沿,这些模型计算密集度较低,延迟较低,可能为传统 transformer 提供更便宜、更快的训练和推理替代方案。

自 20 世纪 60 年代以来,专注于动态、连续系统的 SSM 已经存在,但最近才应用于离散的端到端序列建模。

线性复杂性也使得 SSM 成为长上下文建模的绝佳选择,我们看到几家公司在这方面蓬勃发展。

尽管早期结果显示在各种属性上具有令人印象深刻的效率,研究人员还需要证明现在在 transformer 生态系统中视为理所当然的各种属性(如控制、对齐、推理)。

此外,几何深度学习领域的突破性研究,包括类别深度学习和图神经网络,正在为研究人员提供结构化推理的方法。

尽管这一领域已经存在了相当一段时间,但在这一波新的 AI 浪潮中,它重新引起了兴趣,因为几何方法通常使深度学习算法能够考虑嵌入在现实世界数据中的几何结构(如代码中的抽象语法树、生物通路等),并可应用于各种领域。

此外,除了通用模型,目前还有许多团队在训练特定用途的模型,如代码生成、生物学、视频、图像、语音、机器人技术、音乐、物理、脑电波等,这为模型层增加了另一个多样性和灵活性的向量。


2、模型部署和推理的创新

计算层是 AI 基础设施堆栈中最复杂的层之一,
大型企业和初创企业都在计算层领域创新,加剧了其复杂性。计算层的复杂不仅因为它是一个核心层,也是由于它为堆栈的其他部分提供动力:

它融合了硬件(例如 GPU 和定制的硬件)、软件(例如操作系统、驱动程序、配置工具、框架、编译器以及监控和管理软件)以及商业模型之间的创新和交互。

在硬件层面,随着供应链短缺的缓解,GPU 成本正在下降。下一代 GPU,如 NVIDIA 的 H100 和 B100 系列,结合互连技术的进步,在模型层面扩展了数据和 GPU 并行性。

除了硬件,各种算法和基础设施创新也在实现新的 AI 能力。例如,transformer 架构中的自注意力机制由于其高计算需求,尤其是二次时间和空间复杂性,已成为一个关键瓶颈。

为了解决这些挑战,机器学习系统社区已经发布了各种模型和基础设施层的研究:自注意力机制的演变(如Ring Attention)、KV Cache 优化(如通道量化、剪枝、近似)等。

这些创新减少了 LLM 解码步骤的内存占用,实现了更快的推理、更长的上下文和成本效益。

在我们向个性化、更便宜的微调方法迈进的过程中,仍有许多问题有待解决。

LoRA 等方法释放了内存,实现了经济高效的微调,但事实证明很难对 GPU 资源进行可扩展的管理,以便为微调模型提供服务(GPU 的利用率往往很低,将权重复制进内存和从内存复制出会降低算术强度)。

虽然在批处理、量化和无服务器信息堆栈的更高层次上进行了改进,使基础架构变得更加简便易行,但仍有许多 "悬而未决 "的问题。

Skypilot 和 vLLM 等项目,以及 Modal、Together AI、Fireworks 和 Databricks 等公司,都在推动这方面的发展。

在这一层的供应商对利用其服务的 AI 应用公司的单位经济(尤其是毛利率)产生了巨大影响,我们预计这些动态将继续推动基于下游应用需求的创新。


3、前沿模型训练和开发技术

如前所述,AI 研究正以惊人的速度推进,特别是我们正处于一个令人兴奋的时期,新 AI 方法和技术在预训练、训练和开发方面都在蓬勃发展。

新方法每天都在被开发,与现有方法的演变并行,这意味着 AI 基础设施堆栈正在动态定义和重新定义。

我们看到这些技术在各个方面的扩散,推进 LLM 和扩散模型在基础性能参数(如准确性和延迟)方面的输出,直至推动新前沿的极限(如推理、多模态、垂直特定知识,甚至代理 AI 或新兴能力)。

我们在第一节中强调了一些架构范式,但其他技术示例如下:

  • 微调和对齐:监督反馈、专门的训练数据或精炼权重以适应特定任务(如 RLHF、constitutional AI、PEFT)

  • 检索增强生成(RAG):通过检索机制将 LLM 连接到外部知识源,结合生成功能与搜索和/或整合相关知识库数据的能力

  • 提示范式:一种互动过程,其中 LLM 被指示和引导达到期望的结果(如少样本学习、多样本上下文学习、退后提示、CoT、ToT)

  • 模型混合和合并:混合单独的 AI 模型子网络共同执行任务的机器学习方法(如 MoE、SLERP、DARE、TIES、frankenmerging)

  • 训练稳定性:关于归一化方法(如 LayerNorm vs。RMSNorm)、归一化、激活和其他属性的决策会影响训练稳定性和性能

  • 参数效率:
    影响模型能力和效率的各种方法,如高效的持续预训练

尽管这些方法在实验简便性与效果之间存在权衡,但我们预测这些技术将激发新的发展,随着研究人员更快迭代并解决现实世界的可扩展性和适用性问题。

此外,在应用 AI 中常见的是部署混合或组合技术,但最终,能带来最大效益的方法可能会主导应用 AI 领域。

此外,随着基础模型的不断改进以及更多 AI 驱动解决方案在生产中和现实世界约束下的部署,形势正在动态演变。

最终,我们认为现在仍处于早期阶段,尚未真正建立起霸权,特别是在企业 AI 领域。

因此,我们非常高兴能与开发、启用或商业化这些技术的公司合作,因为这些公司将重塑和重新构想我们如何在现实中构建、开发、操作和部署 AI 模型和应用,并为 AI 公司形成关键的工具层。


4、AI 时代的 DataOps 2.0

我们在文章开头提到,数据和 AI 输出是密不可分的。

我们看到这一点在许多方面得到了体现,从数据质量影响 AI 输出(垃圾进垃圾出),到最近的 AI 创新从以前未开发的数据源(如非结构化数据)中释放洞察力,再到专有数据作为 AI 原生公司的竞争优势和护城河。

在我们的《Data Shift Right》文章中探讨了这种关系,并在最近的《数据指南》中强调了公司利用的新数据策略,以优化 AI 的竞争优势。

鉴于这些催化剂,数据运维面临新的需求,导致存储、标注、流水线、准备和转换的新方法和框架的出现。一些令人兴奋的例子包括:

  • 在预处理阶段,我们看到专为操作 LLM 数据而设计的数据管理和 ETL 解决方案的兴起。
  • 新数据类型(如Embedding)的出现激发了全新的数据运维类别,如向量数据库。
  • 数据标注在 AI 时代不断发展,融合了先进的以数据为中心的方法,这加快了之前的手动或弱监督方法的速度,并吸引了更多非技术最终用户。

  • AI 革命推动了处理各种数据模态(特别是非结构化数据,如视频和图像)的工具的主流应用。许多最先进的工具现在已集成到日常工作流程中。以前处理这些模态是具有挑战性且通常是定制的,导致组织无法完全从这些丰富的数据源中获得价值。

  • 随着组织利用模型训练和推理技术的创新(参见第三节),新的企业工具链和数据工作流程(例如 RAG 堆栈)正在出现。

正如现代数据堆栈推动了数据运维领域标志性十角兽(指成立不到10年但市值超过100亿美元的公司)的崛起,我们相信,专注于 AI 工作流程的新一代数据运维巨头将会出现。


5、下一代可观测性

随着每一波新技术的浪潮,可观察性也随之采取了各种形式(例如,现代数据堆栈中的数据可观察性、用于云应用程序开发的 APM)。

同样,我们看到可观察性在 AI 时代不断发展——一系列新的供应商正在出现,帮助公司监控模型和人工智能应用程序的性能。

虽然我们已经看到许多公司进入市场解决一个关键问题,无论是在前期制作(例如,LLM 评估、测试)、后期制作(例如,监控、捕捉偏差和偏差、可解释性),甚至延伸到相邻的功能,例如模型安全性和合规性、智能路由和缓存;

我们预计(并且已经看到)这些公司的长期路线图将汇聚到创建端到端可观察性平台,创建单一事实来源用于在制作前和制作后环境中的模型性能。

我们对在 AI 可观测性领域出现类似 Datadog 的结果感到兴奋——然而,鉴于环境不断变化,新的模型、新的训练/微调技术和新型应用的出现,在可观测性领域获胜可能需要一支能够快速提供高产品速度的团队,可能比其他领域更甚。

正如我们从 Datadog 的崛起中了解到的那样,该公司能够在十几个(类似的)竞争对手中脱颖而出,因为他们专注于:

  • 快速执行广泛的产品和能力集;

  • 构建 Datadog 可以监控的深度覆盖;

  • 提供广泛的集成支持,以便将尽可能多的邻近系统带入其生态系统。

我们很高兴能与这代初创公司合作,他们在 AI 堆栈中承担这样的任务。


6、编排

随着新兴的 LLM 和生成 AI 应用公司不断增长,我们看到编排层的公司有重大机会成为 AI 开发的支柱。

作为 AI 开发生命周期中的“乐队指挥”,并负责确保和协调 AI 应用的开发、部署、集成和一般管理,编排供应商是一个关键(并且重要的是,保证供应商中立,即所有信息在中立仿真平台上都绝对安全可靠,合作项目中的任何一方都只能访问到跟他们相关的信息。)的集中枢纽,协调开发者遇到的各种 AI 工具的扩展。

Langchain 和 LlamaIndex 等公司在 LLM 领域早期崭露头角,强大的开源生态系统推动了这些公司的采用。

他们创建了框架,为开发人员提供了一套最佳实践和工具包,用于开发自己的 LLM 应用,抽象了连接正确的数据源到模型、实施检索方法等方面的复杂性。

除了 LLM,我们还看到一个供应商生态系统为基于代理的应用创建编排解决方案,进一步简化了新型创新代理 AI 应用的开发过程。

类似于 ReAct 简化 Web 开发的成功,我们预期 AI 编排供应商有类似的机会简化开发,并赋予大众开发各种 AI 应用(如 LLM、代理、计算机视觉等)的能力。

三、AI 基础设施业务存在巨大机遇

正如马克·吐温曾经说过的:“当每个人都在寻找黄金时,是从事镐和铲子生意的好时机。”

我们相信,为机器学习构建“镐和铲子”有着巨大的机会,这将会催生一大批价值数十亿美元的公司来为企业提供实现 AI 运营化的工具和基础设施。