【华为云技术分享】【技术总结】从Hash索引到LSM树

摘要:本文将从实现最简单的Key-Value数据库讲起,然后针对实现过程中遇到的一些瓶颈,采用上述的索引技术,对数据库进行优化,以此达到对数据库的索引技术有一个较为深刻的理解。

 

前言

数据库算是软件应用系统中最常用的一类组件了,不管是一个庞大而复杂的电商系统,还是一个简单的个人博客,多多少少都会用到数据库,或是存储海量的数据,或是存储简单的状态信息。一般地,我们都喜欢将数据库划分为关系型数据库和非关系型数据库(又称NoSQL数据库),前者的典型代表是MySQL数据库,后者的典型代表是HBase数据库。不管是关系型,还是非关系型,数据库都离不开两个最基本的功能:(1)数据存储;(2)数据查询。简单来说就是,当你把数据丢给数据库时,它能够保持下来,并在稍后你想获取的时候,把数据返回给你。

围绕着这两个基本功能,各类数据库都运用了很多技术手段对其进行了优化,其中最广为人知的当属数据库索引技术。索引是一种数据结构,它在牺牲少量数据存储(写)性能的情况下,可以大幅提升数据查询(读)性能。索引也有很多种类型,Hash索引算是最简单高效的一种了,但是由于它自身的限制,在数据库系统中并不被广泛使用。当今最常用的索引技术是B/B+树索引,被广泛地应用在关系型数据库中,主要应用于读多写少的场景。随着NoSQL数据库的兴起,LSM(Log-Structured Merged-Tree)树也逐渐流行,并被Google的BigTable论文所发扬光大。严格来说,LSM树并不算一种传统意义上的索引,它更像是一种设计思想,主要应用于写多读少的场景。

本系列文章,将从实现最简单的Key-Value数据库讲起,然后针对实现过程中遇到的一些瓶颈,采用上述的索引技术,对数据库进行优化,以此达到对数据库的索引技术有一个较为深刻的理解。

 

最简单的数据库

Martin Kleppmann在《Designing Data-Intensive Applications》一书中给出了一个最简单数据库的实现:

#!/bin/bash
db_set() {
  echo "$1,$2" >> database
}

db_get() {
  grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}

这不到10行的shell代码实现了一个简单的Key-Value数据库。它一共有两个函数,db_set和db_get,前者对应数据存储功能,后者对应数据查询功能。该数据库采用简单的文本格式(database文件)进行数据存储,每条记录包含了一个键值对,key和value之间通过逗号(,)进行分隔。

数据库的使用方法也很简单,通过调用db_set key value可以将key及其对应的value存储到数据库中;通过db_get key可以得到该key对应的value:

$ db_set 123456 '{"name":"London","attractions":["Big Ben","London Eye"]}' 
$ db_set 42 '{"name":"San Francisco","attractions":["Golden Gate Bridge"]}'
$ db_get 42
{"name":"San Francisco","attractions":["Golden Gate Bridge"]}

透过db_set的实现我们发现,该数据库每次写都是直接往database文件中追加记录,鉴于文件系统顺序写的高效,因此该数据库的数据写入具有较高的性能。但是追加写也意味着,对同一个key进行更新时,其对应的旧的value并不会被覆盖,这也使得每次调用db_get获取某个key的value时,总是需要遍历所有记录,找到所有符合条件的value,并取其中最新的一个。因此,该数据库的读性能是非常低的。

最简单的数据库读写操作

下面我们采用Java对这个最简单的数据库进行重写:

/**
 * SimpleKvDb.java
 * 追加写
 * 全文件扫描读
 */
public class SimpleKvDb implements KvDb {
    ...
    @Override
    public String get(String key) {
        try (Stream<String> lines = logFile.lines()) {
            // step1: 筛选出该key对应的的所有value(对应grep "^$1," database)
            List<String> values = lines
                    .filter(line -> Util.matchKey(key, line))
                    .collect(Collectors.toList());
            // step2: 选取最新值(对应 sed -e "s/^$1,//" | tail -n 1)
            String record = values.get(values.size() - 1);
            return Util.valueOf(record);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    @Override
    public void set(String key, String value) {
        // step1: 追加写(对应 echo "$1,$2" >> database)
        String record = Util.composeRecord(key, value);
        logFile.append(record);
    }
    ...
}

使用JHM进行压测结果如下:

Benchmark                                            Mode  Cnt  Score   Error  Units
SimpleKvDbReadBenchmark.simpleKvDb_get_10000_test    avgt    8  0.631 ± 0.033  ms/op // 读耗时,1w条记录
SimpleKvDbReadBenchmark.simpleKvDb_get_100000_test   avgt    8  7.020 ± 0.842  ms/op // 读耗时,10w条记录
SimpleKvDbReadBenchmark.simpleKvDb_get_1000000_test  avgt    8  62.562 ± 5.466 ms/op // 读耗时,100w条记录
SimpleKvDbWriteBenchmark.simpleKvDb_set_test         avgt    8  0.036 ± 0.005  ms/op // 写耗时

从结果可以看出,该数据库的实现具有较高的写性能,但是读性能很低,而且读耗时会随着数据量的增加而线性增长。

那么如何优化SimpleKvDb的读性能?引入索引技术!

索引(index)是从数据库中的数据衍生出来的一种数据结构,它并不会对数据造成影响,只会影响数据库的读写性能。对于读操作,它能快速定位到目的数据,从而极大地提升读性能;对于写操作,由于需要增加额外的更新索引操作,因此会略微降低写性能。

如前言所述,索引也有很多类型,每种索引的特点都各有差别。因此到底要不要采用索引,具体采用哪种索引,需要根据实际的应用场景来决定。

下面,我们将首先采用最简单高效的Hash索引对SimpleKvDb进行优化。

 

给数据库加上Hash索引

考虑到Key-Value数据库本身就类似于Hash表的这个特点,我们很容易想到如下的索引策略:在内存中维护一个Hash表,记录每个key对应的记录在数据文件(如前文所说的database文件)中的字节位移(byte offset)。

对于写操作,在往数据文件追加记录后,还需要更新Hash表;对于读操作,首先通过Hash表确定该key对应记录在数据文件中的位移,然后通过字节位移快速找到value在数据文件中的位置并读取,从而避免了全文遍历这样的低效行为。

加上索引之后的读写操作

给SimpleKvDb加上Hash索引,对应的代码实现为:

/**
 * HashIdxKvDb.java
 * 追加写
 * 使用Hash索引提升读性能
 */
public class HashIdxKvDb implements KvDb {
    // 数据存储文件
    private final LogFile curLog;
    // 索引,value为该key对应的数据在文件中的offset
    private final Map<String, Long> idx;
    ...
    @Override
    public String get(String key) {
        if (!idx.containsKey(key)) {
            return "";
        }
        // step1: 读取索引
        long offset = idx.get(key);
        // step2: 根据索引读取value
        String record = curLog.read(offset);
        return Util.valueOf(record);
    }
    @Override
    public void set(String key, String value) {
        String record = Util.composeRecord(key, value);
        long curSize = curLog.size();
        // step1: 追加写数据
        if (curLog.append(record) != 0) {
            // step2: 更新索引
            idx.put(key, curSize);
        }
    }
  ...
}

在实现HashIdxKvDb之前,我们将数据存储文件抽象成了LogFile对象,它的两个基本方法是append(追加写记录)和read(根据offset读记录),其中,read函数通过RandomAccessFile的seek方法快速定位到记录所处的位置,具体实现如下:

// 追加写的日志文件,存储数据库数据
class LogFile {
    // 文件所在路径
    private Path path;
  ...
    // 向日志文件中写入一行记录,自动添加换行符
    // 返回成功写入的字节大小
    long append(String record) {
        try {
            record += System.lineSeparator();
            Files.write(path, record.getBytes(), StandardOpenOption.APPEND);
            return record.getBytes().length;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }
    // 读取offset为起始位置的一行
    String read(long offset) {
        try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "r")) {
            file.seek(offset);
            return file.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }
    ...
}

使用JHM进行压测结果如下:

Benchmark                                              Mode  Cnt  Score   Error  Units
HashIdxKvDbReadBenchmark.hashIdxKvDb_get_10000_test    avgt    8  0.021 ± 0.001  ms/op // 读耗时,1w条记录
HashIdxKvDbReadBenchmark.hashIdxKvDb_get_100000_test   avgt    8  0.021 ± 0.001  ms/op // 读耗时,10w条记录
HashIdxKvDbReadBenchmark.hashIdxKvDb_get_1000000_test  avgt    8  0.021 ± 0.001  ms/op // 读耗时,100w条记录
HashIdxKvDbWriteBenchmark.hashIdxKvDb_set_test         avgt    8  0.038 ± 0.005  ms/op // 写耗时

从压测结果可以看出,相比SimpleKvDb,HashIdxKvDb的读性能有了大幅的提升,而且读耗时不再随着数据量的增加而成线性增长;而且写性能并没有明显的下降。

虽然Hash索引实现很简单,却是极其的高效,它只需要1次磁盘寻址(seek操作),加上1次磁盘I/O(readLine操作),就能将数据加载出来。如果数据之前已经加载到文件系统缓存里,甚至都不用磁盘I/O。

数据合并——compact

到目前为止,不管是SimpleKvDb,还是HashIdxKvDb,写操作都是不断在一个文件中追加数据,这种存储方式,通常我们称之为append-only log。

那么,如何才能避免append-only log无休止的一直扩张下去,直到磁盘空间不足呢?

append-only log的一个显著特点是旧的记录不会被覆盖删除,但是这些数据往往是无用的,因为读取某个key的value时,数据库都是取其最新的值。因此,解决该问题的一个思路是把这些无用的记录清除掉:

(1)当往append-only log追加数据到达一定大小后,另外创建一个新的append-only log进行追加。在这种机制下,数据分散到多个append-only log文件中存储,我们称这些log文件为segment file。

(2)保证只有当前的current segment file是可读可写,old segment file只读不写。

segment file机制

(3)对old segment file进行compact操作——只保留每个key对应的最新记录,把的老记录删除。

单文件compact操作

compact操作往往是在后台线程中执行,数据库会将合并的结果写到一个新的compacted segment file中,这样在执行compact操作时,就不会影响从old segment file中读数据的逻辑。等到compact操作完成之后,再把old segment file删除,后续的读操作迁移到compacted segment file上。

现在,单个compacted segment file中的key都是唯一的,但是多个compacted segment file之间还是有可能存在重复的key,我们还能够更近一步,对多个compacted segment file再一次进行compact操作,这样数据量会再次减少。

多文件compact操作

类似的compact操作可以一层层执行下去,比如,可以对level2 compacted segment file进行compact,生成level3 compacted segment file。但是并不是compact的层次越多越好,具体的compact策略需要结合实际的应用场景进行设计。

 

给HashIdxKvDb加上compact机制

下面,我们试着给HashIdxKvDb加上compact机制。由于在compact机制下,数据会分散在多个segment file中存储,因此之前的Hash索引机制不再适用,我们需要给每个segment file都单独维护一份Hash索引。当然,这样也比较容易实现,只需要维护一个Hash表,key为segment file,value为该segment file对应的Hash索引。

多segment file下的hash索引

对应的代码实现如下:

// 多文件哈希索引实现
class MultiHashIdx {
    ...
    private Map<LogFile, Map<String, Long>> idxs;
    // 获得指定LogFile中,Key的索引
    long idxOf(LogFile file, String key) {
        if (!idxs.containsKey(file) || !idxs.get(file).containsKey(key)) {
            return -1;
        }
        return idxs.get(file).get(key);
    }

    // 添加指定LogFile中Key的索引
    void addIdx(LogFile file, String key, long offset) {
        idxs.putIfAbsent(file, new ConcurrentHashMap<>());
        idxs.get(file).put(key, offset);
    }
    ...
}

另外,我们还需要在CompactionHashIdxKvDb里分别维护一份old segment file、level1 compacted segment file和level2 compacted segment file集合,并通过ScheduledExecutorService定时对这些集合进行compact操作。

/**
 * 追加写,当当前segemnt file到达一定大小后,追加到新到segment file上。并定时对旧的segment file进行compact。
 * 为每个segment file维持一个哈希索引,提升读性能
 * 支持单线程写,多线程读
 */
public class CompactionHashIdxKvDb implements KvDb {
    ...
    // 当前追加写的segment file路径
    private LogFile curLog;

    // 写old segment file集合,会定时对这些文件进行level1 compact合并
    private final Deque<LogFile> toCompact;
    // level1 compacted segment file集合,会定时对这些文件进行level2 compact合并
    private final Deque<LogFile> compactedLevel1;
    // level2 compacted segment file集合
    private final Deque<LogFile> compactedLevel2;
    // 多segment file哈希索引
    private final MultiHashIdx idx;

    // 进行compact的定时调度
    private final ScheduledExecutorService compactExecutor;
    ...
}

相比于HashIdxKvDb, CompactionHashIdxKvDb 在写入新的数据之前,需要判断当前文件大小是否写满,如果写满了,则需要创建新的LogFile进行追加,并将写满后的LogFile归档到toCompact队列中。

@Override
public void set(String key, String value) {
    try {
        // 如果当前LogFile写满了,则放到toCompact队列中,并创建新的LogFile
        if (curLog.size() >= MAX_LOG_SIZE_BYTE) {
            String curPath = curLog.path();
            Map<String, Long> oldIdx = idx.allIdxOf(curLog);
            curLog.renameTo(curPath + "_" + toCompactNum.getAndIncrement());
            toCompact.addLast(curLog);
            // 创建新的文件后,索引也要更新
            idx.addAllIdx(curLog, oldIdx);
            curLog = LogFile.create(curPath);
            idx.cleanIdx(curLog);
        }

        String record = Util.composeRecord(key, value);
        long curSize = curLog.size();
        // 写成功则更新索引
        if (curLog.append(record) != 0) {
            idx.addIdx(curLog, key, curSize);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

CompactionHashIdxKvDb 的读操作相对麻烦,因为数据被分散在多个segment file上,所以需要按照如下顺序完成数据查找,直到查询到为止:当前追加的LogFile->toCompact队列->compactedLevel1队列->compactedLevel2队列。因此,CompactionHashIdxKvDb 的读操作在极端情况下(所查询的数据存储在comactedLevel2队列上时)也会较为低效。

@Override
public String get(String key) {
    // 第一步:从当前的LogFile查找
    if (idx.idxOf(curLog, key) != -1) {
        long offset = idx.idxOf(curLog, key);
        String record = curLog.read(offset);
        return Util.valueOf(record);
    }
    // 第二步:从toCompact中查找
    String record = find(key, toCompact);
    if (!record.isEmpty()) {
        return record;
    }
    // 第三步:从 compactedLevel1 中查找
    record = find(key, compactedLevel1);
    if (!record.isEmpty()) {
        return record;
    }
    // 第四步:从 compactedLevel2 中查找
    record = find(key, compactedLevel2);
    if (!record.isEmpty()) {
            return record;
    }
    return "";
}

对toCompact队列里的old segment file进行单文件level1 compact操作时,可以利用Hash索引。因为Hash索引上所对应的记录总是最新的,因此只需遍历Hash索引,将每个key对应的最新记录查询出来,写到新的level1 compacted segment file中即可。

// 进行level1 compact,对单个old segment file合并
void compactLevel1() {
    while (!toCompact.isEmpty()) {
        // 创建新的level1 compacted segment file
        LogFile newLogFile = LogFile.create(curLog.path() + "_level1_" + level1Num.getAndIncrement());
        LogFile logFile = toCompact.getFirst();
        // 只保留每个key对应的最新的value
        idx.allIdxOf(logFile).forEach((key, offset) -> {
            String record = logFile.read(offset);
            long curSize = newLogFile.size();
            if (newLogFile.append(record) != 0) {
                idx.addIdx(newLogFile, key, curSize);
            }
        });
        // 写完后存储到compactedLevel1队列中,并删除toCompact中对应的文件
        compactedLevel1.addLast(newLogFile);
        toCompact.pollFirst();
        logFile.delete();
    }
}

对compactedLevel1队列进行多文件level2 compact的策略是:将当前队列里所有的level1 compacted segment file合并成一个level2 compacted segment file。具体步骤如下:

1、生成一份compactedLevel1队列的snapshot。目的是为了避免在level2 compact过程中,有新的level1 compacted segment file加入到队列造成影响。

2、对snapshot进行compact操作,按照从新到旧的顺序,将level1 compacted segment file中的记录写入新的level2 compacted segment file中,如果发现level2 compacted segment file已经存在该key,则跳过。

3、等完成任务后,再从compactedLevel1队列里删除已经合并过的level1 compacted segment file。

// 进行level2 compact,针对compactedLevel1队列中所有的文件进行合并
void compactLevel2() {
    ...
    // 生成一份快照
    Deque<LogFile> snapshot = new LinkedList<>(compactedLevel1);
    if (snapshot.isEmpty()) {
        return;
    }
    int compactSize = snapshot.size();
    // level2的文件命名规则为:filename_level2_num
    LogFile newLogFile = LogFile.create(curLog.path() + "_level2_" + level2Num.getAndIncrement());
    while (!snapshot.isEmpty()) {
        // 从最新的level1 compacted segment file开始处理
        LogFile logFile = snapshot.pollLast();
        logFile.lines().forEach(record -> {
            String key = Util.keyOf(record);
            // 只有当前level2 compacted segment file中不存在的key才写入
            if (idx.idxOf(newLogFile, key) == -1) {
                // 写入成功后,更新索引
                long offset = newLogFile.size();
                if (newLogFile.append(record) != 0) {
                    idx.addIdx(newLogFile, key, offset);
                }
            }
        });
    }
    compactedLevel2.addLast(newLogFile);
    // 写入完成后,删除compactedLevel1队列中相应的文件
    while (compactSize > 0) {
        LogFile logFile = compactedLevel1.pollFirst();
        logFile.delete();
        compactSize--;
    }
    ...
}

 

总结

本文首先介绍了Martin Kleppmann给出的最简单的数据库,并使用Java语言对它进行了重新的实现,接着采用Hash索引和compact机制对其进行了一系列的优化。

SimpleKvDb采用append-only log的方式存储数据,append-only log最主要的优点是具备很高的写入性能(文件系统的顺序写比随机写要快很多)。追加写也意味着同一个key对应的旧记录不会被覆盖删除,因此在查询数据时,需要遍历整个文件,找到该key的所有记录,并选取最新的一个。因为涉及到全文遍历,因此SimpleKvDb的读性能非常低。

为了优化SimpleKvDb的读性能,我们实现了具有Hash索引的HashIdxKvDb。Hash索引是一个在内存中维护的Hash表,保存每个key对应的记录在文件中的offset。因此每次数据查询只需要1次磁盘寻址,加上1次磁盘I/O,非常高效。

为了解决append-only log一直扩张导致磁盘空间不足的问题,我们给HashIdxKvDb引入了compact机制,实现了CompactionHashIdxKvDb。compact机制能够有效清理数据库中的无效的旧数据,从而减缓了磁盘空间的压力。

Hash索引虽然简单高效,但是有如下两个限制:

1、必须在内存中维护Hash索引。如果选择在磁盘上实现Hash索引,那么将会带来大量的磁盘随机读写,导致性能的急剧下降。另外,随着compact机制的引入,数据被分散在多个segment file中存储,我们不得不为每个segment file维护一份Hash索引,这就导致Hash索引的内存占用量不断增加,给系统带来了很大的内存压力。

2、区间查询非常低效。比如,当需要查询数据库中范围在[key0000, key9999]之间的所有key时,必须遍历索引中所有的元素,然后找到符合要求的key。

针对Hash索引的这两个限制,要怎样进行优化呢?本系列的下一篇文章,我们将介绍另一种不存在这两个限制的索引——LSM树。

 

点击这里→了解更多精彩内容

 

相关推荐

数据湖&数据库,别再傻傻分不清了

数据管理服务DAS 之 数据库自动化运维功能展播4:慢SQL

云原生数据库三驾马车之TaurusDB

激发数据裂变新动能,HDC.Cloud云数据库前沿技术解读

Entity Framework Core 捕获数据库变动

相关推荐

鸿蒙案例上新啦 | 购物、听歌、健身:15个模板带你一键激活社交娱乐

页面层级如何组织、跨页共享状态如何同步、列表与详情及个人中心如何形成稳定闭环。鸿蒙开发实践中心将这些高频消费与互动场景沉淀为可参考的标准模板,提供从业务架构到界面实现的快捷实践入口。更多实战案例欢迎前往鸿蒙开发实践中心案例中心查看并开启云端体验。

华为云官方博客 338

鸿蒙案例上新啦 | 出门打车、到家上课:25个模板带你一屏打通日常生活

如何合理拆分页面组件、如何建立单一可信的状态源、如何让用户在每一步操作中都获得明确反馈。鸿蒙开发实践中心以标准化的应用与元服务模板为入口,将清晰的业务架构与可复用的代码结构直观呈现。不妨立即挑选一个感兴趣的方向进入云端环境,从页面布局、组件封装、状态管理到路由组织,亲手跑通你的鸿蒙实战项目。

华为云官方博客 324

LakeAgent:DWS数据仓库的第二大脑,告别 “翻译式运维”,迈入智能自治时代

和普通 SQL 改写工具不同,LakeAgent 自动做多方案代价对比,主动规避负优化陷阱:补充分区键过滤实现剪枝,扫描分区由 37 收敛至 1,执行代价从 758.28 降至 113.74;未来的数据仓库,将不止能算、能存,更能看懂自己、诊断自己、理解自己。长久以来,数仓的智能都依附于人:信号需要人解读,瓶颈需要人研判,经验困在个体里,无法随底座永续生长。CPU 高、CPU 告警、CPU 诊断、CPU 使用率过高、CPU 满。语法、错误码、计费、性能调优、最佳实践、故障处置等全场景内容。

华为云官方博客 555

如何使用RDS Supabase后端BaaS能力:文件存储(File Storage)

• product-images(公开桶,允许公共读取):策略名称"Public read for product-images",允许SELECT,目标角色anon,策略定义bucket_id = 'product-images'。上传/删除需另配INSERT/DELETE策略,详见SQL。• product-specs(私有桶,仅认证用户访问自己的文件夹):建议拆为上传、读取、删除三条策略,策略定义均为 bucket_id = 'product-specs' 且仅限本人文件夹(详见下文 SQL)。

华为云官方博客 476

如何使用RDS Supabase后端BaaS能力:实时订阅与推送(Realtime)

Realtime 把数据库的每次变更同步到客户端:表里增、删、改一行,订阅者无需刷新页面或主动请求即可秒级收到。三大能力各司其职:• Postgres Changes — 监听表的 INSERT/UPDATE/DELETE,变化即通知• Broadcast — 客户端之间直接互发,适合瞬时通知、协同编辑• Presence — 跟踪频道里“谁在线”典型场景有实时数据展示(订单、库存、监控指标即时刷新)、即时通讯(消息收发、在线名单、“对方正在输入”)、协同编辑与实时通知。

华为云官方博客 464

AI Agent如何通过MCP连接到华为云RDS Supabase应用平台

Schema元数据列出所有可访问的表(按schema分组)。Schema元数据列出表的列及元数据(类型/默认值/约束)。Schema元数据列出所有约束(PK/FK/UNIQUE/CHECK/EXCLUDE)。可按schema/表/类型过滤。Schema元数据列出所有外键关系。可按schema/表过滤。Schema元数据列出所有索引及定义和大小。可按schema/表过滤。Schema元数据列出已应用的数据库迁移记录。Schema元数据列出已安装的PostgreSQL扩展。

华为云官方博客 473

华为大数据Flink让每一 CU 算力,跑出开源 5.74 倍的性能

1. 开源Apache Flink 已成为业界流计算事实标准,但在极端重负载场景下存在性能瓶颈。2. 华为大数据Flink通过多项自研深度优化,在执行计划、算子执行、状态存储三个层面构建协同的优化体系。3. Nexmark Benchmark 测试结果:整体性能提升 5.74 倍,极端重负载场景性能领先最高 18.3 倍。4. 100% 兼容 Apache Flink 1.20,业务代码零改造,无缝迁移。

华为云官方博客 380

LakeAgent实战 | 业务分析篇从一句提问到业务分析

本文以“订单金额变化”分析为例,展示LakeAgent如何通过自然语言提问,结合DataArtsStudio的数据资产与专业技能,帮助业务人员快速定位数据、生成SQL、追踪血缘、生成报告。从问题出发,逐步实现数据发现、变化洞察、原因追溯与团队协作,最终形成可复用的分析流程与企业规则,显著提升数据分析效率与一致性。

华为云官方博客 389

华为云RDS Supabase AI应用已开放公测 :30分钟搭建AI应用后端

认证、存储、实时推送、数据库API——4个后端模块,传统开发需要数周。今天,你只需要30分钟。AI创业公司的全栈开发工程师。上周老板说"下周要上线的AI对话应用,后端你来搞"——认证鉴权、数据库接口、文件存储、实时推送,每一个模块都需要数周搭建,而市场不会等你。小张打开华为云RDS Supabase,30分钟搭好认证、数据库API、对象存储,剩下的时间全用来打磨AI交互体验。基于开源Supabase+华为云RDS PostgreSQL全托管能力,为开发者提供。

华为云官方博客 396

如何使用RDS Supabase后端BaaS能力:数据库函数(Database Function)

上一篇介绍了 Database Data API——建表即自动生成增删改查接口,解决单表读写,但聚合统计、跨表事务、字段自动维护等逻辑已超出其能力边界。本篇推进到数据库函数(Database Function):把业务逻辑封装进数据库,前端通过 .rpc() 调用,由触发器自动响应数据变更。上一篇中,小张基于 Data API 完成了产品目录的建表与基础读写。但新需求随之而来:按分类统计产品数与价格区间、上架新品同时写产品表与定价表、产品修改后自动刷新更新时间。

华为云官方博客 464

如何使用RDS Supabase后端BaaS能力:创表即生成API(Database Data API)

认证、存储、实时推送、数据库API——4个后端模块,传统开发要数周。本系列逐个拆解,首篇聚焦数据库API(Database Data API):建表即得 API,无需手写后端。小张,AI创业公司的全栈工程师。老板让他做个产品目录页,展示产品与价格、支持上下架。传统做法要搭后端、写接口、配权限,工期少说1-2周;而在华为云RDS Supabase,建两张表、应用 SDK 直连,几分钟增删改查全跑通。

华为云官方博客 478

从一句话到一个能出去的链接,AI开发者空间做了什么

自行搭建的 Agent、正在使用的开源项目、团队既有的工具,都可以接入,可以通过AI创建或者手动搭建环境;华为云 AI开发者空间——面向开发者的云端一站式开发平台,做的就是把这段路接上:环境免安装,浏览器打开即为完整的 Linux 运行环境,预置的 AI 智能体开箱即用。第一个任务建议选能出结果的:做一个游戏、生成一个页面、创建一台实例,从需求走到可访问的链接,一次就能看清这个环境在做什么。沙箱是一个完整的 Linux 环境,依赖、服务、端口都是真实的,不是模拟。环境释放后,权限随之失效。

华为云官方博客 425

华为云码道配置自定义大模型,四种方式完整实操

案例介绍。

华为云官方博客 624

图数据库:实现穿透式监管的数据基座

穿透式监管标志着国企监管从“管企业”向“管资本”转型过程中的关键一跃。而图数据库正是这一跃的技术支点。从股权穿透到资金链穿透,从供应链穿透到数据穿透,华为GES图数据库正在将“穿透式监管”从理念变为可落地、可操作的实践。无论是天眼查式的全量链路查询,还是某集团财经的税务规则判断,抑或是芯片供应链式的部件风险分析,乃至通过Palantir构建决策知识图谱,其底层逻辑都是一致的——用图的力量,穿透一切形式的“隔离层”,直抵风险本质与责任核心。

华为云官方博客 664

《公共云三十问》正式收官,奔赴企业AI百场景新征程

今年6月底,公共云产业联盟重点推出《公共云三十问》专栏,以每周三问的节奏,连载十周,解读《先进公共云》白皮书,分享国计民生系列案例,旨在凝聚共识、推动落地实践。同时,专栏分析公共云在产业升级、民生福祉、科技创新、全球发展等方面的作用,梳理中国特色公共云发展路径:以普惠云拓展用云广度,以智算云提升用云深度,以自主云夯实用云厚度,以全球云打开用云跨度。这十问从专属云、行业云切入,深入政务、金融、交通、电力、钢铁、化工、油气、制造等国计民生场景,回答公共云“怎么用”。

华为云官方博客 473

开发者小技巧|IAM配置,让码道代码智能体连续30天免登录

案例介绍。

华为云官方博客 645

码道深度对接CodeArts,让需求自动落地为代码

案例介绍。

华为云官方博客 719

【公共云三十问 之三十】拥抱AI,企业如何建设面向智能体的专属云?

企业可分阶段建设覆盖开发、运行、运营及安全的一体化智能体平台——从构建一站式、高低码协同的智能体开发平台,到打造高性能、极致弹性的运行时环境,再到搭建AgentOps驱动的持续运营与运维体系,同时构建端到端、多层次的全链路安全防护体系。在数据湖建设方面,遵循“存量复用、平滑演进、AI导向、分步迭代”原则,分三阶段推进——打造AI多模数据湖核心能力、完成存算分离与统一元数据管理、实现闭环赋能全面支撑智能体规模化落地,在保护既有投资的前提下将传统数据平台转型为AI数据专属供给平台。

华为云官方博客 823

【公共云三十问 之二十九】国计民生之制造:制造云如何重塑数智时代的新质生产力?

平台打通软件交付的完整路径,覆盖需求管理、代码托管、代码检查、编译构建、制品仓库、部署、测试等关键环节,支持大模型能力融入研发全流程,实现从需求到交付的端到端管理。通过构建“研发一朵云”,方案打破地域与组织壁垒,实现跨团队协同开发,有效缩短软件交付周期,助力工业制造企业从“传统研发”走向“敏捷研发”,加速数智化转型进程。当制造云真正成为数据流通的平台、AI创新的底座,公共云的社会价值将从基础设施层向生产场景层持续延伸,为新型工业化建设和制造强国战略注入源源不断的动力,重塑数智时代的中国制造新质生产力。

华为云官方博客 789

告别重复手工迁仓,码道MCP+Skill实现仓库批量迁移

案例介绍。

华为云官方博客 790

【公共云三十问 之二十八】国计民生之油气:智算云如何叩响数智时代的“地心蓝金”?

西南油气田以“向地球深部要资源”为战略方向,联合以华为云Stack为代表的公共云服务商共同打造统一算力底座,建设西南区域数据湖与通用技术平台,构建“勘探开发工程技术、天然气生产过程管控、天然气产运储销”等三个一体化协同应用场景,支撑油气勘探、开发生产、管输储运等全业务链的数智化转型。当智算云真正成为数据流通的平台、AI创新的底座,油气田得以将算力效能、数据价值与智能技术深度融入勘探开发全链条,将“看不见的算力”转化为“摸得着的储量”,以智能化手段持续降低勘探风险、缩短发现周期、提高采收效率,确保生产安全。

华为云官方博客 613

【公共云三十问 之二十七】国计民生之化工:数智云如何重塑传统化工新范式?

在架构方面,方案构建云边协同的统一体系,涵盖AI协同、数据协同与运营协同三大维度——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现“边用边学”的智能闭环,模型平均精度提升20%以上。此外,方案提供一站式企业级工业物联网平台、数据湖与数据治理平台,覆盖工业数据分析“采、存、算、管、用”全流程,打通生产现场与管理系统间的数据壁垒,有效消除数据孤岛。层面,方案充分发挥软硬协同优势,实现计算、存储、网络与云平台的深度协同,训练线性度超95%,为大模型训练推理提供强劲算力支撑。▲化工“数智”新模式。

华为云官方博客 663

【公共云三十问 之二十六】国计民生之钢铁:数智云如何淬炼中国钢铁新力量?

同时,构建云边端协同的统一架构,支撑集团总部与各生产基地两级协同发展——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现训推一体化的资源调度与高效协同,为钢铁行业的数智化转型构建了坚实的算力底座。层面,方案构建覆盖“工业数采→边缘IT/OT融合→统一数据湖→数据治理平台→数据应用”全链路的一站式数据治理与流通平台,打通生产现场与管理系统间的数据壁垒,让数据“采得到、流得动、用得好”,加速数据价值从生产现场向经营决策层流动释放。湘潭钢铁集团的钢铁数智云,是钢铁工业智能云建设的标杆案例。

华为云官方博客 526

LakeWatch——MRS 智能运维Agent

从资源池、队列、作业等多个维度,对集群CPU与内存资源进行精细化分析与对比,直观呈现不同时间段的空闲、满载等使用状态,识别资源浪费,指导治理优化,为扩缩容决策提供数据支撑。对HDFS存储进行多维度深度分析(目录、用户、用户组等),涵盖使用量、增量、文件大小分布及数据热度等指标,帮助快速识别增长较快的目录、长期未访问的文件,统计目录文件数量与小文件占比,评估HDFS总存储量与扩缩容需求,为存储治理与资源规划提供全面数据支撑。Agent主动巡检,识别异常趋势并提前预警,让运维从"事后处理"转向"事前预防"。

华为云官方博客 515

华为云专题文档发布:丰富的AI案例、选型指导和概念解读

发布 “云服务选型”、“云计算概念”专题,提供云服务选型指导,助您选择匹配业务的云服务,解读云计算相关概念,开启云上学习之路。开发者工具和文档专区上线,提供从学习、实践到反馈的全链路支持。帮助中心新增100余篇AI案例。

华为云官方博客 286

一个人,也能用AI做点“星东西”

华为云星耀创想者大赛-2026华为云OPC创意大赛等你来~

华为云官方博客 290

从需求到可执行工作项:MCP+Skill 让智能体规划真正落地

回顾整个体验,以前让智能体帮我拆需求,它是洋洋洒洒给我列了一大篇 Markdown,看着挺像那么回事,但最后还是得我一条条往 CodeArts Req 里搬,搬完再手动挂父子关系、排日期,拆需求五分钟,搬运两小时。现在好了,MCP 给智能体接上了"手",Skill 给它装上了"项目经理的脑子",一句话下去,34 个工作项连带排期、层级关系全给我建好了,甘特图一拉,齐刷刷都在两周内,节省了多少工作量啊,美滋滋。本次实操在码道代码智能体基础上,构建了一套"需求→可执行工作项"的自动化规划方案。

华为云官方博客 576

华为云码道「码力续航计划」更新!

✅上新GLM‑5.3‑Flash,高速响应,代码生成、工程解析、Agent调试高效输出。新增GLM‑5.3‑Flash福利模型,每日解锁1000万免费Tokens。● 本次活动共发放总量价值100万元Tokens,发完即止!把耗算力的编码重活,交给华为云码道代码智能体🚀🚀。● 每日额度准时刷新,当日清零、次日满血重置。✅复杂算法、大项目解析、批量代码生成随便跑。✅不用抠额度、不用算消耗,放开调试大胆迭代。✅无需额外操作,登录即送,直接切换模型使用。● 把算力交给码道,让撸码不再内耗👏🏻。

华为云官方博客 685

【公共云三十问 之二十四】国计民生之交通:交通云如何铺就数智通衢新坦途?

交通行业作为支撑实体经济的关键基础设施,涵盖公路、铁路、机场、港口、地铁等众多子领域,其中,公路网络作为综合交通运输体系的基础骨架,承担着全国超过70%的客货运量,其数字化、智能化转型的需求尤为迫切。方面,提供Agent开发框架、知识库管理、RAG工程、意图识别、静态应用编排及模型评测等能力,支持客户将行业知识、业务流程与AI能力深度融合,快速构建面向收费稽核、路网调度、应急处置等场景的智能应用。开发并实现落地应用,全面覆盖建设、管理、养护、运营、服务、安全、办公七大业务领域。

华为云官方博客 461

【公共云三十问 之二十五】国计民生之电力:电力云如何点亮数智电力新引擎?

方案构建了覆盖数据获取、质量评估到标准化处理的全链路能力。基于公共云服务商智算底座及气象大模型,通过时空图神经网络重新建模产流机制,中国华电建成超50TB多源融合数据集,突破时空建模与机理融合等五大关键技术,短期径流预测精度较传统机理模型提升5个百分点以上,实现15天中期径流预测精度超85%,推理效率达秒级。基于气象大模型的基础架构与预训练能力,客户可直接在大模型之上开展增量训练与微调,充分利用场站所在区域的高精度历史数据(如3公里、1公里分辨率),针对不同场站和区域的地形地貌与气候特征进行模型调优。

华为云官方博客 381
上一篇: 【华为云技术分享】云图说|应用编排服务AOS,助力应用上云自动化
下一篇: 【华为云技术分享】六步教你玩转DevOps上华为云DevCloud实践
华为云开发者联盟
华为云开发者联盟 企业官方账号 企业官方账号
博客等级 码龄10年 72万+粉丝 5985原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值