工作实践:11种API性能优化方法

一、索引优化

接口性能优化时,大家第一个想到的通常是:优化索引。

确实,优化索引的成本是最小的。

你可以通过查看线上日志或监控报告,发现某个接口使用的某条SQL语句耗时较长。

此时,你可能会有以下疑问:

这条SQL语句是否已经加了索引?
加的索引是否生效了?
MySQL是否选择了错误的索引?

1.1 没加索引

在SQL语句中,忘记为WHERE条件的关键字段或ORDER BY后的排序字段加索引是项目中常见的问题。

在项目初期,由于表中的数据量较小,加不加索引对SQL查询性能影响不大。

然而,随着业务的发展,表中的数据量不断增加,这时就必须加索引了。

可以通过以下命令查看/添加索引:

show index from `table_name`
CREATE INDEX index_name ON table_name (column_name);

这种方式能够显著提高查询性能,尤其是在数据量庞大的情况下。

1.2 索引没生效

通过上述命令我们已经确认索引是存在的,但它是否生效呢?

此时,你可能会有这样的疑问。

那么,如何查看索引是否生效呢?

答案是:可以使用 EXPLAIN 命令,查看 MySQL 的执行计划,它会显示索引的使用情况。

例如:

EXPLAIN SELECT * FROM `order` WHERE code='002';

结果:

这个命令将显示查询的执行计划,包括使用了哪些索引。

如果索引生效,你会在输出结果中看到相关的信息。

通过这几列可以判断索引使用情况,执行计划包含列的含义如下图所示:

说实话,SQL语句没有使用索引,除去没有建索引的情况外,最大的可能性是索引失效了。

以下是索引失效的常见原因:

了解这些原因,可以帮助你在查询优化时避免索引失效的问题,确保数据库查询性能保持最佳。

1.3 选错索引

此外,你是否遇到过这样一种情况:明明是同一条SQL语句,只是入参不同。

有时候使用的是索引A,有时候却使用索引B?

没错,有时候MySQL会选错索引。

必要时可以使用 FORCE INDEX 来强制查询SQL使用某个索引。

例如:

SELECT * FROM `order` FORCE INDEX (index_name) WHERE code='002';

至于为什么MySQL会选错索引,原因可能有以下几点:

了解这些原因,可以帮助你更好地理解和控制MySQL的索引选择行为,确保查询性能的稳定性。

二、SQL优化

如果优化了索引之后效果不明显,接下来可以尝试优化一下SQL语句,因为相对于修改Java代码来说,改造SQL语句的成本要小得多。

以下是SQL优化的15个小技巧:

三、远程调用

多时候,我们需要在一个接口中调用其他服务的接口。

例如,有这样的业务场景:

在用户信息查询接口中需要返回以下信息:用户名称、性别、等级、头像、积分和成长值。

其中,用户名称、性别、等级和头像存储在用户服务中,积分存储在积分服务中,成长值存储在成长值服务中。为了将这些数据统一返回,我们需要提供一个额外的对外接口服务。

因此,用户信息查询接口需要调用用户查询接口、积分查询接口和成长值查询接口,然后将数据汇总并统一返回。

调用过程如下图所示:

调用远程接口总耗时 530ms = 200ms + 150ms + 180ms

显然这种串行调用远程接口性能是非常不好的,调用远程接口总的耗时为所有的远程接口耗时之和。

3.1 串行改并行

上面说到,既然串行调用多个远程接口性能很差,为什么不改成并行呢?

如下图所示:

调用远程接口的总耗时为200ms,这等于耗时最长的那次远程接口调用时间。

在Java 8之前,可以通过实现Callable接口来获取线程的返回结果。

在Java 8之后,可以通过CompletableFuture类来实现这一功能。

以下是一个使用CompletableFuture的示例:

public class RemoteServiceExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 调用用户服务接口
        CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟远程调用
            simulateDelay(200);
            return "User Info";
        });
        // 调用积分服务接口
        CompletableFuture<String> pointsFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟远程调用
            simulateDelay(150);
            return "Points Info";
        });
        // 调用成长值服务接口
        CompletableFuture<String> growthFuture = CompletableFuture.supplyAsync(() -> {
            // 模拟远程调用
            simulateDelay(100);
            return "Growth Info";
        });
        // 汇总结果
        CompletableFuture<Void> allOf = CompletableFuture.allOf(userFuture, pointsFuture, growthFuture);
        // 等待所有异步操作完成
        allOf.join();
        // 获取结果
        String userInfo = userFuture.get();
        String pointsInfo = pointsFuture.get();
        String growthInfo = growthFuture.get();
    }
}

3.2 数据异构

为了提升接口性能,尤其在高并发场景下,可以考虑数据冗余,将用户信息、积分和成长值的数据统一存储在一个地方,比如Redis。

这样,通过用户ID可以直接从Redis中查询所需的数据,从而避免远程接口调用

但需要注意的是,如果使用了数据异构方案,就可能会出现数据一致性问题。

用户信息、积分和成长值有更新的话,大部分情况下,会先更新到数据库,然后同步到redis。

但这种跨库的操作,可能会导致两边数据不一致的情况产生。

四、重复调用

在我们的日常工作代码中,重复调用非常常见,但如果没有控制好,会严重影响接口的性能。

让我们一起来看看这个问题。

4.1 循环查数据库 有时候,我们需要从指定的用户集合中查询出哪些用户已经存在于数据库中。

一种实现方式如下:

public List<User> findExistingUsers(List<String> userIds) {
    List<User> existingUsers = new ArrayList<>();
    for (String userId : userIds) {
        User user = userRepository.findById(userId);
        if (user != null) {
            existingUsers.add(user);
        }
    }
    return existingUsers;
}

上述代码会对每个用户ID执行一次数据库查询,这在用户集合较大时会导致性能问题。

我们可以通过批量查询来优化性能,减少数据库的查询次数。

public List<User> findExistingUsers(List<String> userIds) {
    // 批量查询数据库
    List<User> users = userRepository.findByIds(userIds);
    return users;
}

这里有个需要注意的地方是:id集合的大小要做限制,最好一次不要请求太多的数据。要根据实际情况而定,建议控制每次请求的记录条数在500以内。

五、异步处理

在进行接口性能优化时,有时候需要重新梳理业务逻辑,检查是否存在设计不合理的地方。

假设有一个用户请求接口,需要执行以下操作:

  1. 业务操作

  2. 发送站内通知

  3. 记录操作日志 为了实现方便,通常会将这些逻辑放在接口中同步执行,但这会对接口性能造成一定影响。

这个接口表面上看起来没有问题,但如果你仔细梳理一下业务逻辑,会发现只有业务操作才是核心逻辑,其他的功能都是非核心逻辑。

在这里有个原则就是:

核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。

上面这个例子中,发站内通知和用户操作日志功能,对实时性要求不高,即使晚点写库,用户无非是晚点收到站内通知,或者运营晚点看到用户操作日志,对业务影响不大,所以完全可以异步处理。

异步处理方案

异步处理通常有两种主要方式:多线程和消息队列(MQ)

5.1 线程池异步优化

使用线程池改造之后,接口逻辑如下

5.2 MQ异步

使用线程池有个小问题就是:如果服务器重启了,或者是需要被执行的功能出现异常了,无法重试,会丢数据。

为了避免使用线程池处理异步任务时出现数据丢失的问题,可以考虑使用更加健壮和可靠的异步处理方案,如消息队列(MQ)。消息队列不仅可以异步处理任务,还能够保证消息的持久化和可靠性,支持重试机制。

使用mq改造之后,接口逻辑如下

六、避免大事务

很多小伙伴在使用Spring框架开发项目时,为了方便,喜欢使用@Transactional注解提供事务功能。

没错,使用@Transactional注解这种声明式事务的方式提供事务功能,确实能少写很多代码,提升开发效率。

但也容易造成大事务,引发性能的问题。

为了避免大事务引发的问题,可以考虑以下优化建议:

  1. 少用@Transactional注解

  2. 将查询(select)方法放到事务外

  3. 事务中避免远程调用

  4. 事务中避免一次性处理太多数据

  5. 有些功能可以非事务执行

  6. 有些功能可以异步处理

七、锁粒度

在一些业务场景中,为了避免多个线程并发修改同一共享数据而引发数据异常,通常我们会使用加锁的方式来解决这个问题。

然而,如果锁的设计不当,导致锁的粒度过粗,也会对接口性能产生显著的负面影响。

7.1 synchronized

在Java中,我们可以使用synchronized关键字来为代码加锁。

通常有两种写法:在方法上加锁和在代码块上加锁。

1. 方法上加锁

public synchronized void doSave(String fileUrl) {
    mkdir();
    uploadFile(fileUrl);
    sendMessage(fileUrl);
}

在方法上加锁的目的是为了防止并发情况下创建相同的目录,避免第二次创建失败而影响业务功能。

但这种直接在方法上加锁的方式,锁的粒度较粗。

因为doSave方法中的文件上传和消息发送并不需要加锁,只有创建目录的方法需要加锁。

我们知道,文件上传操作非常耗时,如果将整个方法加锁,那么需要等到整个方法执行完之后才能释放锁。

显然,这会导致该方法的性能下降,得不偿失。

2. 代码块上加锁我们可以将加锁改在代码块上,从而缩小锁的粒度, 如下:

public void doSave(String path, String fileUrl) {
    synchronized(this) {
        if (!exists(path)) {
            mkdir(path);
        }
    }
    uploadFile(fileUrl);
    sendMessage(fileUrl);
}

这样改造后,锁的粒度变小了,只有并发创建目录时才加锁。

创建目录是一个非常快的操作,即使加锁对接口性能的影响也不大。

最重要的是,其他的文件上传和消息发送功能仍然可以并发执行。

多节点环境中的问题 在单机版服务中,这种做法没有问题。但在生产环境中,为了保证服务的稳定性,同一个服务通常会部署在多个节点上。如果某个节点挂掉,其他节点的服务仍然可用。

多节点部署避免了某个节点挂掉导致服务不可用的情况,同时也能分摊整个系统的流量,避免系统压力过大。

但这种部署方式也带来了新的问题:synchronized只能保证一个节点加锁有效。

7.2 Redis分布式锁

在分布式系统中,由于Redis分布式锁的实现相对简单且高效,因此它在许多实际业务场景中被广泛采用。

使用Redis分布式锁的伪代码如下:

public boolean doSave(String path, String fileUrl) {
    try {
        String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
        if ("OK".equals(result)) {
            if (!exists(path)) {
                mkdir(path);
                uploadFile(fileUrl);
                sendMessage(fileUrl);
            }
            return true;
        }
    } finally {
        unlock(lockKey, requestId);
    }
    return false;
}

与之前使用synchronized关键字加锁时一样,这里的锁的范围也太大了,换句话说,锁的粒度太粗。这会导致整个方法的执行效率很低。

实际上,只有在创建目录时才需要加分布式锁,其余代码不需要加锁。

于是,我们需要优化代码:

public void doSave(String path, String fileUrl) {
    if (tryLock()) {
        try {
            if (!exists(path)) {
                mkdir(path);
            }
        } finally {
            unlock(lockKey, requestId);
        }
    }
    uploadFile(fileUrl);
    sendMessage(fileUrl);
}

private boolean tryLock() {
    String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
    return "OK".equals(result);
}

private void unlock(String lockKey, String requestId) {
    // 解锁逻辑
}

上面的代码将加锁的范围缩小了,只有在创建目录时才加锁。这样的简单优化后,接口性能可以得到显著提升。

7.3 数据库锁

MySQL数据库中的三种锁

  1. 表锁

    • 优点:加锁快,不会出现死锁。

    • 缺点:锁定粒度大,锁冲突的概率高,并发度最低。

  2. 行锁

    • 优点:锁定粒度最小,锁冲突的概率低,并发度最高。

    • 缺点:加锁慢,会出现死锁。

  3. 间隙锁

    • 优点:锁定粒度介于表锁和行锁之间。

    • 缺点:开销和加锁时间介于表锁和行锁之间,并发度一般,也会出现死锁。

锁与并发度

并发度越高,接口性能越好。因此,数据库锁的优化方向是:

  1. 优先使用行锁

  2. 其次使用间隙锁

  3. 最后使用表锁

八、分页处理

有时候需要调用某个接口来批量查询数据,例如,通过用户ID批量查询用户信息,然后为这些用户赠送积分。

但是,如果一次性查询的用户数量太多,例如一次查询2000个用户的数据,传入2000个用户的ID进行远程调用时,用户查询接口经常会出现超时的情况。

调用代码如下:

List<User> users = remoteCallUser(ids);

众所周知,调用接口从数据库获取数据需要经过网络传输。如果数据量过大,无论是数据获取速度还是网络传输速度都会受到带宽限制,从而导致耗时较长。

优化使用:分页处理。

将一次性获取所有数据的请求,改为分多次获取,每次只获取一部分用户的数据,最后进行合并和汇总。

其实,处理这个问题可以分为两种场景:同步调用和异步调用。

8.1 同步调用

如果在job中需要获取2000个用户的信息,它要求只要能正确获取到数据即可,对获取数据的总耗时要求不高。

但对每一次远程接口调用的耗时有要求,不能大于500ms,否则会有邮件预警。

这时,我们可以同步分页调用批量查询用户信息接口。

具体示例代码如下:

List<List<Long>> allIds = Lists.partition(ids, 200);

for (List<Long> batchIds : allIds) {
    List<User> users = remoteCallUser(batchIds);
}

代码中我使用了Google Guava工具中的Lists.partition方法,用它来做分页简直太好用了,不然要写一大堆分页的代码。 8.2 异步调用 如果是在某个接口中需要获取2000个用户的信息,需要考虑的因素更多。

除了远程调用接口的耗时,还需要考虑该接口本身的总耗时,也不能超过500ms。

这时,使用上面的同步分页请求远程接口的方法肯定是行不通的。

那么,只能使用异步调用了。

代码如下:

List<List<Long>> allIds = Lists.partition(ids, 200);

final List<User> result = Lists.newArrayList();
allIds.stream().forEach(batchIds -> {
    CompletableFuture.supplyAsync(() -> {
        result.addAll(remoteCallUser(batchIds));
        return Boolean.TRUE;
    }, executor);
});

使用CompletableFuture类,通过多个线程异步调用远程接口,最后汇总结果统一返回。

九、加缓存

通常情况下,我们最常用的缓存是:Redis和Memcached。

但对于Java应用来说,绝大多数情况下使用的是Redis,所以接下来我们以Redis为例。

在关系型数据库(例如:MySQL)中,菜单通常有上下级关系。某个四级分类是某个三级分类的子分类,三级分类是某个二级分类的子分类,而二级分类又是某个一级分类的子分类。

这种存储结构决定了,想一次性查出整个分类树并非易事。这需要使用程序递归查询,而如果分类很多,这个递归操作会非常耗时。

因此,如果每次都直接从数据库中查询分类树的数据,会是一个非常耗时的操作。

这时我们可以使用缓存。在大多数情况下,接口直接从缓存中获取数据。操作Redis可以使用成熟的框架,比如:Jedis和Redisson等。 使用Jedis的伪代码如下:

String json = jedis.get(key);
if (StringUtils.isNotEmpty(json)) {
    CategoryTree categoryTree = JsonUtil.toObject(json);
    return categoryTree;
}
return queryCategoryTreeFromDb();

十、分库分表

有时候,接口性能受限的并不是其他方面,而是数据库。

当系统发展到一定阶段,用户并发量增加,会有大量的数据库请求,这不仅需要占用大量的数据库连接,还会带来磁盘IO的性能瓶颈问题。

此外,随着用户数量的不断增加,产生的数据量也越来越大,一张表可能无法存储所有数据。由于数据量太大,即使SQL语句使用了索引,查询数据时也会非常耗时。

那么,这种情况下该怎么办呢?

答案是:需要进行分库分表。

如下图所示:

图中将用户库拆分成了三个库,每个库都包含了三张用户表。

如果有用户请求过来,先根据用户ID路由到其中一个用户库,然后再定位到某张表。

路由的算法有很多:

  1. 根据ID取模

    • 例如:ID=7,有3张表,则7%3=1,模为1,路由到用户表1。

  2. 给ID指定一个区间范围

    • 例如:ID的值是0-10万,则数据存在用户表0;ID的值是10-20万,则数据存在用户表1。

  3. 一致性Hash算法。 分库分表主要有两个方向:垂直和水平。

1. 垂直分库分表
垂直分库分表(即业务方向)更简单,将不同的业务数据存储在不同的库或表中。

例如,将用户数据和订单数据存储在不同的库中。

2. 水平分库分表
水平分库分表(即数据方向)上,分库和分表的作用有区别,不能混为一谈。

分库
  • 目的:解决数据库连接资源不足问题和磁盘IO的性能瓶颈问题。

分表
  • 目的:解决单表数据量太大,SQL语句查询数据时,即使走了索引也非常耗时的问题。此外,还可以解决消耗CPU资源的问题。

分库分表
  • 目的:综合解决数据库连接资源不足、磁盘IO性能瓶颈、数据检索耗时和CPU资源消耗等问题。

业务场景中的应用

  1. 只分库

    • 用户并发量大,但需要保存的数据量很少。

  2. 只分表

    • 用户并发量不大,但需要保存的数据量很多。

  3. 分库分表

    • 用户并发量大,并且需要保存的数据量也很多。

十一、监控功能

优化接口性能问题,除了上面提到的这些常用方法之外,还需要配合使用一些辅助功能,因为它们真的可以帮我们提升查找问题的效率。

11.1 开启慢查询日志

通常情况下,为了定位SQL的性能瓶颈,我们需要开启MySQL的慢查询日志。把超过指定时间的SQL语句单独记录下来,方便以后分析和定位问题。

开启慢查询日志需要重点关注三个参数:

  • slow_query_log:慢查询开关

  • slow_query_log_file:慢查询日志存放的路径

  • long_query_time:超过多少秒才会记录日志

通过MySQL的SET命令可以设置:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/usr/local/mysql/data/slow.log';
SET GLOBAL long_query_time = 2;

设置完之后,如果某条SQL的执行时间超过了2秒,会被自动记录到slow.log文件中。

当然,也可以直接修改配置文件my.cnf:

[mysqld]
slow_query_log = ON
slow_query_log_file = /usr/local/mysql/data/slow.log
long_query_time = 2

但这种方式需要重启MySQL服务。

很多公司每天早上都会发一封慢查询日志的邮件,开发人员根据这些信息优化SQL。

11.2 加监控

为了在出现SQL问题时能够及时发现,我们需要对系统做监控。

目前业界使用比较多的开源监控系统是:Prometheus

它提供了监控和预警的功能。

架构图如下:

我们可以用它监控如下信息:

  • 接口响应时间

  • 调用第三方服务耗时

  • 慢查询sql耗时

  • cpu使用情况

  • 内存使用情况

  • 磁盘使用情况

  • 数据库使用情况

  • 等等。。。

它的界面大概长这样子:

可以看到MySQL的当前QPS、活跃线程数、连接数、缓存池的大小等信息。

如果发现连接池占用的数据量太多,肯定会对接口性能造成影响。

这时可能是由于代码中开启了连接却忘记关闭,或者并发量太大导致的,需要进一步排查和系统优化

11.3链路跟踪

有时候,一个接口涉及的逻辑非常复杂,例如查询数据库、查询Redis、远程调用接口、发送MQ消息以及执行业务代码等等。

这种情况下,接口的一次请求会涉及到非常长的调用链路。如果逐一排查这些问题,会耗费大量时间,此时我们已经无法用传统的方法来定位问题。

有没有办法解决这个问题呢?

答案是使用分布式链路跟踪系统:SkyWalking

SkyWalking的架构图如下:

在SkyWalking中,可以通过traceId(全局唯一的ID)来串联一个接口请求的完整链路。你可以看到整个接口的耗时、调用的远程服务的耗时、访问数据库或者Redis的耗时等,功能非常强大。

之前没有这个功能时,为了定位线上接口性能问题,我们需要在代码中加日志,手动打印出链路中各个环节的耗时情况,然后再逐一排查。这种方法不仅费时费力,而且容易遗漏细节。

如果你用过SkyWalking来排查接口性能问题,你会不自觉地爱上它的功能。如果你想了解更多功能,可以访问SkyWalking的官网:www.skywalking.apache.org

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mistral AI最新力作——Mistral Large媲美GPT-4

Mistral AI自豪地宣布&#xff0c;他们的最新力作——Mistral Large&#xff0c;已经正式面世。这款尖端的文本生成模型不仅在多语言理解上表现出色&#xff0c;更在推理能力上达到了顶级水平。Mistral Large能够处理包括文本理解、转换和代码生成在内的复杂多语言推理任务。 M…

go-admin-ui开源后台管理系统华为云部署

1.华为云开通8000与9527端口 2.编译 编译成功 3.发布到远程服务器 4.登陆华为云终端 5.安装Nginx 6.查看服务启动状态 7.添加网站 添加与修改配置www-data 改为 www 自定义日志输出格式 添加网站配置文件go_admin_ui.conf 添加如下内容: location 下的root指向网站文件夹 修…

former系列在时间序列预测任务上的研究综述

总&#xff1a;基于Transformer的LSTF解决方案 现有基于Transformer的LSTM解决方案设计要素总结如下&#xff1a; 从图中可以看出&#xff0c;Transformer在时序中应用具体包含以下几个步骤&#xff1a; 1&#xff09;时序分解&#xff1a;对于数据处理&#xff0c;TSF中0均…

zookeeper + kafka消息队列

zookeeper kafka 消息队列 一、消息队列简介 1、什么是消息队列 消息队列&#xff08;Message Queue&#xff09;是一种用于跨进程或分布式系统中传递消息的通信机制。消息队列在异步通信、系统解耦、负载均衡和容错方面具有重要作用。 &#xff08;1&#xff09;特性 异步…

“打造智能售货机系统,基于ruoyi微服务版本生成基础代码“

目录 # 开篇 1. 菜单 2. 字典配置 3. 表配置 3.1 导入表 3.2 区域管理 3.3 合作商管理 3.4 点位管理 4. 代码导入 4.1 后端代码生成 4.2 前端代码生成 5. 数据库代码执行 6. 点位管理菜单顺序修改 7. 页面展示 8. 附加设备表 8.1 新增设备管理菜单 8.2 创建字…

GBDT算法详解

GBDT算法详解 梯度提升决策树&#xff08;Gradient Boosting Decision Trees&#xff0c;GBDT&#xff09;是机器学习中一种强大的集成算法。它通过构建一系列的决策树&#xff0c;并逐步优化模型的预测能力&#xff0c;在各种回归和分类任务中取得了显著的效果。本文将详细介…

【mysql 安装启动失败】 没有网下 libssl.so.10 not found 如何解决?

问题描述&#xff1a; libssl.so.10 > not found libcrypto.so.10 > not found [rootmysql tools]# ls -l /usr/sbin/mysqld -rwxr-xr-x. 1 root root 64290024 Sep 14 2022 /usr/sbin/mysqld [rootmysql tools]# ldd /usr/sbin/mysqldlinux-vdso.so.1 (0x00007fff97105…

Blazor 组件:创建、生命周期、嵌套和 UI 集成

在本文中&#xff0c;您将获得以下问题的答案。 什么是 Blazor 组件&#xff1f;如何使用组件&#xff1f;Blazor 组件的生命周期是什么&#xff1f;我们可以从一个组件调用另一个组件吗&#xff1f;如何创建 Blazor 组件&#xff1f;在组件中哪里写 C# 代码&#xff1f; 什么…

碳化硅陶瓷膜的生产工艺和应用

一、生产工艺 碳化硅陶瓷膜的生产工艺多样&#xff0c;其中浸渍提拉法和喷涂法为两大主流技术。 浸渍提拉法 浸渍提拉法是一种广泛应用的制备方法。其过程主要包括&#xff1a;先将陶瓷颗粒或者聚合物前体分散在水或有机溶剂中&#xff0c;形成均质稳定的制膜液。随后&#xff…

深入探索C++中的AVL树

引言 在数据结构和算法的世界里&#xff0c;平衡二叉搜索树&#xff08;Balanced Binary Search Tree, BST&#xff09;是一种非常重要的数据结构。AVL树&#xff08;Adelson-Velsky和Landis发明的树&#xff09;就是平衡二叉搜索树的一种&#xff0c;它通过自平衡来维护其性质…

ELK+Filebeat+kafka+zookeeper构建海量日志分析平台

ELK是什么&#xff08;What&#xff09;&#xff1f; ELK组件介绍 ELK 是ElasticSearch开源生态中提供的一套完整日志收集、分析以及展示的解决方案&#xff0c;是三个产品的首字母缩写&#xff0c;分别是ElasticSearch、Logstash 和 Kibana。除此之外&#xff0c;FileBeat也是…

海外版coze前端代码助手

定位 解决前端同事的开发问题 参数配置 测试 支持 最屌的大模型及语音播报。 体验地址 海外版前端代码助手 需要魔法才能体验油

索尼MXF文件断电变2G恢复方法(PXW-Z280V)

PXM-Z280V算是索尼比较经典的机型&#xff0c;也是使用MXF文件格式的机型之一。近期接到很多例索尼MXF量突然不正常的案例&#xff08;如变成512字节或者2G&#xff09;&#xff0c;下面来看下这个案例。 故障存储: 128G存储卡 /文件系统&#xff1a;exFAT 故障现象: 客户反…

Centos SFTP搭建

SFTP配置、连接及挂载教程_sftp连接-CSDN博客1、确认是否安装yum list installed | grep openssh-server 2、创建用户和组 sudo groupadd tksftpgroup sudo useradd -g tksftpgroup -d /home/www/tk_data -s /sbin/nologin tksftp01 sudo passwd tksftp013. 配置SFTP注意&a…

设置浏览器互不干扰

目录 一、查看浏览器文件路径 二、 其他盘新建文件夹Cache 三、以管理员运行CMD 四、执行命令 一、查看浏览器文件路径 chrome://version/ 二、 其他盘新建文件夹Cache D:\chrome\Cache 三、以管理员运行CMD 四、执行命令 Mklink /d "C:\Users\Lenovo\AppData\Loca…

国产化ETL产品必备的特性(非开源包装)

ETL负责将分布的、异构数据源中的数据如关系数据、平面数据文件等抽取到临时中间层后进行抽取、清洗&#xff08;净化&#xff09;、转换、装载、标准、集成&#xff08;汇总&#xff09;...... 最后加载到数据仓库或数据集市中&#xff0c;成为联机分析处理、数据挖掘的基础。…

关键属性描述ASYNC_REG

关键属性描述 属性信息 本章提供有关XilinxVivadoDesign Suite属性的信息。条目 每个属性包含以下信息&#xff08;如适用&#xff09;&#xff1a; •物业说明&#xff0c;包括其主要用途。 •支持该特性的Xilinx FPGA体系结构&#xff0c;包括UltraScale™ 架构设备&#xff…

数据结构【二叉树】

前言 我们在前面学习了使用数组来实现二叉树&#xff0c;但是数组实现二叉树仅适用于完全二叉树&#xff08;非完全二叉树会有空间浪费&#xff09;&#xff0c;所以我们本章讲解的是链式二叉树&#xff0c;但由于学习二叉树的操作需要有一颗树&#xff0c;才能学习相关的基本…

2024.6.23周报

目录 摘要 ABSTRACT 一、文献阅读 一、题目 二、摘要 三、网络架构 四、创新点 五、文章解读 1、Introduction 2、Method 3、实验 4、结论 二、代码实验 总结 摘要 本周阅读了一篇题目为NAS-PINN: NEURAL ARCHITECTURE SEARCH-GUIDED PHYSICS-INFORMED NEURAL N…

生成式AI和LLM的一些基本概念和名词解释

1. Machine Learning 机器学习是人工智能&#xff08;AI&#xff09;的一个分支&#xff0c;旨在通过算法和统计模型&#xff0c;使计算机系统能够从数据中学习并自动改进。机器学习算法使用数据来构建模型&#xff0c;该模型可用于预测或决策。机器学习应用于各种领域&#x…