说出这11种API接口性能优化,面试官一定会对你刮目相看

前言

接口性能优化是后端开发人员经常碰到的一道面试题,因为它是一个跟开发语言无关的公共问题。

这个问题既可以很简单,也可以相当复杂。

有时候,只需要添加一个索引就能解决。
有时候,代码需要进行重构。
有时候,必须增加缓存。
有时候,需要引入一些中间件,例如消息队列(MQ)。
有时候,需进行分库分表。
有时候,需要拆分服务。
等等。

导致接口性能问题的原因多种多样,不同项目的不同接口,其原因可能各不相同。

这里,小北给大家做一下系统化、体系化的梳理,使大家在面试过程中能够清晰、有条理的回答出面试官的提问,让面试官 “眼前一亮、口水直流”,然后实现”offer直提”。

插播一条:如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题。

一、索引优化

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

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

你可以通过查看线上日志或监控报告,发现某个接口使用的某条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的索引选择行为,确保查询性能的稳定性。

插播一条:如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题。

二、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改造之后,接口逻辑如下

插播一条:如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题。

六、避免大事务

很多小伙伴在使用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. 最后使用表锁

插播一条:如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题。

八、分页处理

有时候,我需要调用某个接口来批量查询数据,例如,通过用户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,有4张表,则7%4=3,模为3,路由到用户表3。
  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、活跃线程数、连接数、缓存池的大小等信息。

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

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

链路跟踪

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

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

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

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

SkyWalking的架构图如下:

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

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

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

总结

认真看到这里的同学,相信已经对API接口性能优化有一个清晰的、系统的认知了,如果在面试中能够完整的说出这11种API接口性能优化的思路,相信面试官一定会对你刮目相看的。

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

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

相关文章

Vite: 集成Lint工具规范代码

概述 在前端开发中&#xff0c;尤其是在大型项目中&#xff0c;代码的规范性和一致性对于项目的可维护性、可读性以及团队协作效率至关重要。为了保障代码质量&#xff0c;前端社区涌现出了许多Lint工具&#xff0c;如ESLint、Prettier、Stylelint等&#xff0c;它们能帮助我们…

使用深度远程启动管理器配置BMC DHCP管理地址的方法

目录 1.请确保服务器BMC是DHCP状态&#xff0c;才可以使用深度工具分配地址&#xff1b;若BMC配置过静态地址&#xff0c;请使用静态地址登录&#xff1b; 2.配置好自己笔记本的 ip&#xff08;例如&#xff1a;192.168.78.1&#xff09;&#xff0c;用网线与 ipmi 独立管理口…

Day46

Day46 手写Spring-MVC 解决Controller层的方案 思路&#xff1a;监听器在项目启动时DispatherServlet会将controller层的信息记录下来&#xff0c;当前端发送请求的时候DispatherServlet就会根据信息分发给controller层。 准备工作 准备工作的目的是准备好监听器&#xff0c;而…

AI-智能体基础设施

个性化记忆需要世界模型来协助构建 业界有一个精简的Agent表达公示&#xff0c;即&#xff1a;Agent大模型&#xff08;LLM&#xff09;记忆&#xff08;Memory&#xff09;主动规划&#xff08;Planning&#xff09;工具使用&#xff08;Tool Use&#xff09;。基于该公式&am…

零信任价值获全面认可 新场景下展现无穷潜力

2023年&#xff0c;零信任在全球范围内持续快速发展&#xff0c;已经从新的安全理念发展成为云时代的主流安全架构&#xff0c;进入了全面普及期。 2023年&#xff0c;中国零信任市场同样涨势迅猛&#xff0c;产业生态越来越成熟&#xff0c;应用范围越来越广&#xff0c;应用…

面试题-Redis简介

1.主流应用框架 概念&#xff1a; 穿透查询&#xff1a;数据库中的名词&#xff0c;与逐层查询不同&#xff0c;通过超链接可直接查询想要的结果&#xff0c;更加方便快捷 熔断机制&#xff1a;指软件系统中&#xff0c;由于某些原因使得服务出现了过载现象&#xff0c;为防止…

「2024中国数据要素产业图谱1.0版」重磅发布,景联文科技凭借高质量数据采集服务入选!

近日&#xff0c;景联文科技入选数据猿和上海大数据联盟发布的《2024中国数据要素产业图谱1.0版》数据采集服务板块。 景联文科技是专业数据服务公司&#xff0c;提供从数据采集、清洗、标注的全流程数据解决方案&#xff0c;协助人工智能企业解决整个AI链条中数据采集和数据标…

Maven高级的多环境配置与应用

多环境配置与应用 这一节中&#xff0c;我们会讲两个内容&#xff0c;分别是多环境开发和跳过测试 5.1 多环境开发 我们平常都是在自己的开发环境进行开发&#xff0c;当开发完成后&#xff0c;需要把开发的功能部署到测试环境供测试人员进行测试使用&#xff0c;等测试人员测…

Redis报错:MISCONF Redis is configured to save RDB snapshots

错误提示内容&#xff1a; 2024-06-25 16:30:49 : Connection: Redis_Server > [runCommand] PING 2024-06-25 16:30:49 : Connection: Redis_Server > Response received : -MISCONF Redis is configured to save RDB snapshots, but it is currently not able to pers…

Qt Quick Effect Maker 工具使用介绍

一、介绍 随着 Qt 版本的不断升级,越来越多的功能被加入 Qt,一些新的工具也随之应运而生,Qt Quick Effect Maker 工具是 Qt 6.5 之后才新添加的工具,之前的名字应该是叫做 Qt shader tool 这个模块。 以下是官方的释义:Qt Quick Effect Maker是一个用于为Qt Quick创建自定…

C语⾔数据类型和变量

C语⾔数据类型和变量 1.数据类型介绍1.1 字符型1.2 整型1.3 浮点型1.4 布尔类型1.5 各种数据类型的长度1.5.1 sizeof操作符1.5.2 数据类型长度1.5.3 sizeof中表达式不计算 2. signed 和 unsigned3. 数据类型的取值范围4. 变量4.1 变量的创建4.2 变量的分类 5. 算术操作符&#…

社区团购小程序开发

在快节奏的现代生活中&#xff0c;人们越来越追求便利与效率。社区团购小程序应运而生&#xff0c;以其独特的优势成为连接社区居民与优质商品的重要桥梁。本文将探讨社区团购小程序的特点、优势以及未来发展趋势&#xff0c;为大家揭示这一新型购物模式的魅力。 社区团购小程序…

MAC 查看公钥私钥

电脑配置过公钥私钥&#xff0c;现在需要查看&#xff1a; 1、 查看本地是否存在SSH密钥 命令&#xff1a;ls -al ~/.ssh 如果在输出的文件列表中发现id_rsa和id_rsa.pub的存在&#xff0c;证明本地已经存在SSH密钥&#xff0c;请执行第3步 2、 生成SSH密钥 命令&#xff1…

一本好的电子画册应这样做,你做对了吗?

​一本好的电子画册&#xff0c;不仅要有吸引人的图文&#xff0c;还可能包括视频、音频等多媒体元素&#xff0c;为读者提供全方位的阅读体验。连贯性是指画册的整体设计风格、内容布局要协调一致&#xff0c;让读者在阅读过程中感受到流畅和自然。创新性则要求创作者在内容呈…

39 - 电影评分(高频 SQL 50 题基础版)

39 - 电影评分 (selectu.name as results fromMovieRating m left join Users u on m.user_idu.user_id GROUP BYm.user_id order by count(*) desc,u.name asc limit 1) union all (selectm1.title as results fromMovieRating m left join Movies m1 on m.movie_idm1.movie…

加速业务布局,30年老将加盟ATFX,掌舵运营新篇章

全球领先的差价合约经纪商ATFX日前宣布了一项重大人事任命&#xff0c;聘请业界资深人士约翰博格(John Bogue)为机构业务运营总监。约翰博格是一名行业老将&#xff0c;曾在差价合约界深耕三十余载。伴随其加入ATFX&#xff0c;相信他的深厚专业知识和从业经验将为ATFX机构业务…

数据分析python基础实战分析

数据分析python基础实战分析 安装python&#xff0c;建议安装Anaconda 【Anaconda下载链接】https://repo.anaconda.com/archive/ 记得勾选上这个框框 安装完后&#xff0c;然后把这两个框框给取消掉再点完成 在电脑搜索框输入"Jupyter"&#xff0c;牛马启动&am…

司美格鲁肽在中国获批!深度解析报告附上

在中国&#xff0c;肥胖问题日益严重&#xff0c;但有效的治疗方法却相对匮乏。然而&#xff0c;这一现状随着国家药品监督管理局&#xff08;NMPA&#xff09;对诺和诺德公司研发的司美格鲁肽注射液&#xff08;商品名&#xff1a;诺和盈&#xff09;的批准而得到改变。6月25日…

LabVIEW中卡尔曼滤波的作用与意义

卡尔曼滤波&#xff08;Kalman Filter&#xff09;是一种在控制系统和信号处理领域广泛应用的递推滤波算法&#xff0c;能够在噪声环境下对动态系统的状态进行最优估计。其广泛应用于导航、目标跟踪、图像处理、经济预测等多个领域。本文将详细介绍卡尔曼滤波在LabVIEW中的作用…

pytorch基础知识Tensor算术运算

1、Tensor的基本概念 标量是零维的张量&#xff0c;向量是一维的张量&#xff0c;矩阵是二维的张量 2、Tensor的创建 import torch"""常见的几个tensor创建""" a torch.Tensor([[1,2],[3,4]]) #2行2列的 print(a, a.type()) print(torch.on…