如何让QPS提升20倍

一、什么是QPS

QPS,全称Queries Per Second,即每秒查询率,是用于衡量信息检索系统(例如搜索引擎或数据库)或请求-响应系统(如Web服务器)每秒能够处理的请求数或查询次数的一个性能指标。以下是对QPS的详细解释:

一、定义与意义

  • 定义:QPS表示系统在单位时间(通常为一秒)内能够成功处理的请求数量。在高并发场景下,这个指标尤为重要,因为它直接关系到系统的稳定性和用户体验。
  • 意义:QPS是衡量服务器性能的关键指标之一,它直接反映了系统处理请求的能力。通过监控和优化QPS,可以确保系统在高负载下依然保持高效稳定运行。

二、计算方法

QPS的计算公式为:QPS = 总请求数 / 时间段(秒)。具体计算步骤如下:

确定时间窗口:根据实际需求和服务器负载情况来确定时间窗口,可以是1秒、1分钟、5分钟等。
收集查询日志:收集服务器在该时间窗口内的查询日志,并统计成功处理的请求总数。
计算QPS:将总请求数除以时间窗口的总秒数,即可得到平均每秒的查询率。
例如,如果在1分钟内系统处理了3000个请求,则QPS = 3000 / 60 = 50 QPS,意味着系统平均每秒处理了50个请求。

三、与并发数和响应时间的关系

QPS与系统的并发数和响应时间紧密相关。具体来说:
并发数:并发数是指系统在同一时间内处理的请求数量。并发数越高,系统在单位时间内能够处理的请求数量也就越多,从而可能提高QPS。
响应时间:响应时间是指系统从接收到请求到返回响应结果所需的时间。响应时间越短,系统在单位时间内能够处理的请求数量也就越多,同样可能提高QPS。
因此,在优化系统性能时,可以通过增加并发数和缩短响应时间来提升QPS。

二、同步代码

所谓的同步代码,也就是从我们接受到请求直到请求返回都是由一个线程处理的,如果处理代码中有阻塞那么这个时候此线程就会阻塞,在请求量比较大的情况下,也就是并发场景,这个时候会有很多的请求发过来,那么tomcat只有两百的线程,如果线程阻塞时间较长,那么tomcat的线程会被全部阻塞,导致无法处理外部请求,进而系统的吞吐量就会很低

2.1、示意图

在这里插入图片描述

在这张图片中可以清晰的看到,前端(移动端+pc端)发过来一个请求,这时tomcat会开启一个线程处理,这个线程从接受请求是开启直到请求返回都是一个线程在处理,那么就会存在上面所说的同步阻塞问题
到这里先思考三秒钟,该情况如何优化
这是同步代码的第一个问题。(大家别慌,我们先提出同步代码的所有问题,然后我们一一解决)
接下来继续探索下一个问题
以上我们聊了从接受请求到处理请求都是由一个线程处理,当并发量大并且代码有阻塞的情况下,会将tomcat的线程耗尽,从而达到tomcat的瓶颈。那造成这个问题的原因是什么呢?

  • 第一个:由于tomcat的线程是有限的(200)
  • 第二个:由于处理代码耗时,导致线程阻塞,进而导致tomcat线程耗尽

2.2、同步处理代码图解

在这里插入图片描述
在这张图中,大家可以清晰的看到当需要完成这一个任务时,需要先完成任务1,再完成任务2,然后完成任务3。那么所消耗的时间就是 :time > 任务1 + 任务2 + 任务3,在这里我举个实际生活中的场景,如果你要下单,那么需要调用 用户服务(查询用户信息)—>商品服务(查询商品信息)—>积分服务(修改积分)—>订单服务(生成订单)—>库存服务(减库存)

2.3、代码示例:伪代码模拟

JSONPObject createOrder(Integer userId,Integer goodsId){// 1、调用用户服务,获取用户信息User user = getUserById(userId); // 2s// 2、调用商品服务,获取商品详情Goods goods = getGoodsById(goodsId); // 2s// 3、调用积分服务,修改积分updatePoints(userId);  // 2s// 4、调用订单服务,生成订单createOrderByUserAndGoods(user,goods); // 2s// 5、调用库存服务,修改库存updateInventoryByGoodsId(goodsId);  //2sreturn null;}

这里只给出了个示例,实际中链路会很长,那这个时候是不是需要花费很长的时间,那这里也将是我们需要优化的点

三、异步代码优化

首先我们使用异步代码优化第二个问题,也就是刚刚提到的代码串行所造成的耗时,进而导致的线程阻塞。

3.1、图解异步代码

先来张图
在这里插入图片描述
在这幅图中可以清晰看到只要到我们的处理代码,我们开启了四个线程处理,在这里我将订单服务放到了用户服务和商品服务完成之后处理,这里和你的系统设计有关系,也可以和其他服务同时并发处理,那么经过这次优化后,处理时间 time > 前四个服务中最长的 + 订单服务,这样既完成了代码串行问题的优化。
很多小伙伴在这个时候是不是想着光理论没用,要能代码实现。放心,肯定会有代码实现的。

3.2、代码示例:

JSONPObject createOrder2(Integer userId, Integer goodsId) {// 1、调用用户服务,获取用户信息CompletableFuture<User> future1 = CompletableFuture.supplyAsync(() -> {// 2sreturn getUserById(userId);});// 2、调用商品服务,获取商品详情CompletableFuture<Goods> future2 = CompletableFuture.supplyAsync(() -> {return getGoodsById(goodsId); // 2s});// 3、调用积分服务,修改积分CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {updatePoints(userId);  // 2s});// 4、调用订单服务,生成订单(在用户服务和商品服务调用结束后执行)CompletableFuture<Void> completableFuture = future1.thenCombineAsync(future2, (user, goods) -> {createOrderByUserAndGoods(user, goods); // 2sreturn null;});// 5、调用库存服务,修改库存CompletableFuture.runAsync(() -> {updateInventoryByGoodsId(goodsId);  //2s});return null;}

在这里大家你要纠结为什么用户服务和商品服务完成后调用订单服务,这个和你的业务逻辑有关系,怎么写都无所谓,在这里大量使用了CompletableFuture,接下来我详细介绍一下CompletableFuture

3.3、CompletableFuture 详讲

CompletableFuture是Java 8中引入的一个类,它实现了Future和CompletionStage接口,为异步编程提供了强大的支持。以下是对CompletableFuture的详细介绍:

3.3.1、基本概念与特性

  • 异步执行:CompletableFuture允许任务在后台线程中异步执行,不会阻塞主线程,从而提高了应用程序的响应性和性能。
  • 可组合性:CompletableFuture的操作可以组合成一个或多个CompletableFuture对象,构成复杂的异步计算链。这包括结果的转换、组合以及异常处理等。
  • 异常处理:通过exceptionally()等方法,CompletableFuture可以捕获计算中的异常并返回默认值,或者通过handle()等方法同时处理正常结果和异常。
  • 取消与超时:支持取消异步任务和设置超时时间,避免任务的无限等待。
  • 非阻塞式等待:提供了非阻塞式的等待方法,如join()和getNow(),可以在不阻塞当前线程的情况下获取任务的结果。
  • 并行处理:在处理多个耗时操作时,如I/O操作、数据库访问或网络请求,CompletableFuture可以并行执行这些任务,提高系统吞吐量和响应能力。

3.3.2、创建CompletableFuture实例

1、supplyAsync():用于创建返回结果的异步任务。例如:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {// 执行异步任务并返回结果return "Hello, CompletableFuture!";
});

2、runAsync():用于创建不返回结果的异步任务。例如:

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {// 执行异步任务System.out.println("Task running asynchronously");
});

3.3.3、任务编排方法

3.3.3.1、转换类方法
  • thenApply() / thenApplyAsync():将上一个任务的结果转换为新的结果。thenApply()在同一个线程中执行,而 thenApplyAsync()可能在新的线程中执行。
  • thenAccept() / thenAcceptAsync():处理上一个任务的结果,但不返回新的值。thenAccept()在同一个线程中执行,而thenAcceptAsync()可能在新的线程中执行。
  • thenRun() / thenRunAsync():在上一个任务完成后执行一个操作,不使用上一个任务的结果。
3.3.3.2、组合类方法
  • thenCompose() / thenComposeAsync():将两个CompletableFuture组合成一个。当一个任务依赖另一个任务的结果时,可以使用此方法。
  • thenCombine() / thenCombineAsync():组合两个独立任务的结果。需要两个独立任务的结果进行计算时,可以使用此方法。
3.3.3.3、多任务协调方法
  • allOf():等待所有任务完成。适用于需要等待多个任务都完成的场景。
  • anyOf():等待任意一个任务完成。适用于多个任务中只需要最快的结果的场景。
3.3.3.4、异常处理机制
  • exceptionally():处理异常并提供默认值。当CompletableFuture中的任务抛出异常时,可以捕获该异常并返回一个默认值。
  • handle() / handleAsync():处理正常结果和异常。无论任务是否成功完成,都可以使用此方法处理结果或异常。
  • whenComplete() / whenCompleteAsync():任务完成时的回调(正常或异常)。可以在任务完成后执行一些清理工作或记录日志等。
3.3.3.5、使用示例

以下是一个简单的使用示例,展示了如何创建CompletableFuture对象、进行任务编排以及处理异常:

public class CompletableFutureExample {public static void main(String[] args) {// 创建两个异步任务CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(1000); // 模拟耗时操作return "Result from future1";} catch (InterruptedException e) {Thread.currentThread().interrupt();return "Task interrupted";}});CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(500); // 模拟耗时操作return "Result from future2";} catch (InterruptedException e) {Thread.currentThread().interrupt();return "Task interrupted";}});// 组合两个异步任务的结果CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (result1, result2) -> {return result1 + " and " + result2;});// 处理异常并提供默认值CompletableFuture<String> safeFuture = combinedFuture.exceptionally(ex -> {return "Default value due to error: " + ex.getMessage();});// 获取结果并打印try {String result = safeFuture.get(); // 阻塞等待结果返回System.out.println(result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}

在这个示例中,我们创建了两个异步任务future1和future2,它们分别在不同的线程中执行并返回结果。然后,我们使用thenCombine()方法将这两个任务的结果组合成一个新的CompletableFuture对象combinedFuture。接着,我们使用exceptionally()方法处理可能发生的异常,并提供一个默认值。最后,我们使用get()方法阻塞等待结果返回并打印出来。

综上所述,CompletableFuture是Java异步编程的强大工具,它提供了一种简洁且强大的方式来处理异步任务。通过丰富的API和灵活的任务编排能力,CompletableFuture可以轻松地创建、组合和链式调用异步操作,从而提高了程序的响应速度和资源利用率。
大家看完这里CompletableFuture的介绍后,再回过头去看看我们写的伪代码就知道怎么回事儿了(为什么是异步的,为什么会提高系统执行时间)

3.4、接口异步

在这里我们已经优化了同步代码所造成的线程阻塞问题,那我们如何优化tomcat因线程有限(200)而造成的吞吐量下降问题呢?

首先我们分析一下问题在哪里:1、tomcat线程池有限(200);2、占用tomcat线程时间过长

占用时间过长我们已经做了优化,针对第一个问题,最简单的方法时配置tomcat的线程数量,但是这种方法并不是我们研究的重点。这里我们依然采用异步的方式去解决问题

解决的核心思路:tomcat主线程接受请求------> 交给子线程处理 ----->找tomcat线程返回

3.4.1、图解

在这里这样写大家可能看不懂,上图:
在这里插入图片描述
针对这张图,我i在这里做详细介绍:

前端发起请求,tomcat接受到请求后,通过Spring MVC的DispatcherServlet将请求交给响应的 controller 处理,但这个controller返回的是一个CompletableFuture对象,那么这个时候任务就会交给子线程处理,tomcat 线程将被释放,并且spring boot会开启一个监听器,监听你返回的 CompletableFuture 对象的状态,一旦CompletableFuture对象状态被修改为完成,那么这个时候就会找到tomcat线程返回相应的结果

3.4.2代码示例

controller

 @GetMapping("name")public CompletableFuture<String> getUserName(){return userService.getUserName();}@GetMapping("setName")public void setName(){userService.setUserName();} 

service

 CompletableFuture<String> completableFuture = new CompletableFuture<>();@Overridepublic CompletableFuture<String> getUserName() {return completableFuture;}@Overridepublic void setUserName() {completableFuture.complete("siyu");}

在这里你就会看到你请求name接口时并拿不到数据,当你在请求一下setName接口时name接口就拿到了值,这里就实现了异步操作,当然实际代码中你肯定不会这么用,这只是个示例,实际代码中设置name这一步你可能会用定时任务什么的去实现,我就不过多赘述了。

在这里优化思路已经讲完了。那来个实际优化案例,本例使用(异步+合并)的方式提升系统并发量

四、实际场景优化案例

controller 代码示例

@RestController
@RequestMapping("user")
public class UserController {@Autowiredprivate UserService userService;@GetMappingpublic CompletableFuture<User> getUserById(@RequestParam("userId") Integer userId) throws ExecutionException, InterruptedException {return userService.getUser(userId);}
}

service 代码示例

public interface UserService {CompletableFuture<User> getUser(Integer userId) throws ExecutionException, InterruptedException;
}

serviceImpl 代码示例

@Slf4j
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;private final LinkedBlockingDeque<Request> blockingDeque = new LinkedBlockingDeque<>();private final ExecutorService executorService = Executors.newFixedThreadPool(16);private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(16);@Datastatic class Request {Integer userId;CompletableFuture<User> completableFuture;}@Overridepublic CompletableFuture<User> getUser(Integer userId) {CompletableFuture<User> future = new CompletableFuture<>();Request request = new Request();request.userId = userId;request.completableFuture = future;blockingDeque.add(request);return future;}@PostConstructpublic void init() {AtomicInteger count = new AtomicInteger(0);scheduler.scheduleAtFixedRate(() -> {if (blockingDeque.isEmpty()) return;List<Request> requests = new ArrayList<>();blockingDeque.drainTo(requests);Set<Integer> userIds = requests.stream().map(Request::getUserId).collect(Collectors.toSet());List<User> usersFromDb = userMapper.selectByIds(userIds);log.info("查询数据库{}次,处理{}个请求", count.incrementAndGet(), requests.size());Map<Integer, User> userMap = usersFromDb.stream().collect(Collectors.toMap(User::getUserId, user -> user));for (Request request : requests) {CompletableFuture.runAsync(() ->{User user = userMap.getOrDefault(request.userId, null);request.completableFuture.complete(user);}).exceptionally(ex ->{log.error(ex.getMessage());return null;});}}, 200, 200, TimeUnit.MILLISECONDS);}@PreDestroypublic void destroy() {scheduler.shutdown();try {if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {scheduler.shutdownNow();if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {System.err.println("Scheduler did not terminate!");}}} catch (InterruptedException ex) {scheduler.shutdownNow();Thread.currentThread().interrupt();}executorService.shutdown();try {if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {executorService.shutdownNow();if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {System.err.println("ExecutorService did not terminate!");}}} catch (InterruptedException ex) {executorService.shutdownNow();Thread.currentThread().interrupt();}}
}

首先说明一下,本例中使用的userId,那这个场景肯能不是很多,比如说多人查询同一个热门商品,那就很好用了。

代码设计思想解读:

大量请求发过来后,构建 Request 对象,Request 对象包含请求的userId和一个Completablefuture对象,然后将 Request 放入阻塞队列,等待定时任务处理,接口直接返回Completablefuture对象。
定时任务从阻塞队列中定时弹出所有请求进行处理。拿到请求后,根据 userId 去重,然后调用批量查询接口查询数据,拿到数据后,比对 Request 中的userId和获取到数据的userId,如果相等,将获取后的数据设置到对应Request 的Completablefuture对象。完结散花。

五、祝愿

路漫漫其修远兮,吾将上下而求索。
愿明天的您遇见更好的自己

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

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

相关文章

Domain Adaptation(李宏毅)机器学习 2023 Spring HW11 (Boss Baseline)

1. 领域适配简介 领域适配是一种迁移学习方法,适用于源领域和目标领域数据分布不同但学习任务相同的情况。具体而言,我们在源领域(通常有大量标注数据)训练一个模型,并希望将其应用于目标领域(通常只有少量或没有标注数据)。然而,由于这两个领域的数据分布不同,模型在…

SQL从入门到实战-1

目录 学前须知 sqlzoo数据介绍 world nobel covid ge game、goal、eteam teacher、dept movie、casting、actor 基础语句 select&from 基础查询select单列&多列&所有列&别名应用 例题一 例题二 例题三 select使用distinct去重 例题四 例题五…

Python在Excel工作表中创建数据透视表

在数据处理和分析工作中&#xff0c;Excel作为一个广泛使用的工具&#xff0c;提供了强大的功能来管理和解析数据。当面对大量复杂的数据集时&#xff0c;为了更高效地总结、分析和展示数据&#xff0c;创建数据透视表成为一种不可或缺的方法。通过使用Python这样的编程语言与E…

springboot整合h2

在 Spring Boot 中整合 H2 数据库非常简单。H2 是一个轻量级的嵌入式数据库&#xff0c;非常适合开发和测试环境。以下是整合 H2 数据库的步骤&#xff1a; 1. 添加依赖 首先&#xff0c;在你的 pom.xml 文件中添加 H2 数据库的依赖&#xff1a; <dependency><grou…

Web前端界面开发

前沿&#xff1a;介绍自适应和响应式布局 自适应布局&#xff1a;-----针对页面1个像素的变换而变化 就是我们上一个练习的效果 我们的页面效果&#xff0c;随着我们的屏幕大小而发生适配的效果&#xff08;类似等比例&#xff09; 如&#xff1a;rem适配 和 vw/vh适配 …

【01】AE特效开发制作特技-Adobe After Effects-AE特效制作快速入门-制作飞机,子弹,爆炸特效以及导出png序列图-优雅草央千澈

【01】AE特效开发制作特技-Adobe After Effects-AE特效制作快速入门-制作飞机&#xff0c;子弹&#xff0c;爆炸特效以及导出png序列图-优雅草央千澈 开发背景 优雅草央千澈所有的合集&#xff0c;系列文章可能是不太适合完全初学者的&#xff0c;因为课程不会非常细致的系统…

java项目之在线文档管理系统源码(springboot+mysql+vue+文档)

大家好我是风歌&#xff0c;曾担任某大厂java架构师&#xff0c;如今专注java毕设领域。今天要和大家聊的是一款基于springboot的在线文档管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 在线文档管理系统的主要使用者分为管…

可靠的人形探测,未完待续(III)

一不小心&#xff0c;此去经年啊。问大家新年快乐&#xff01; 那&#xff0c;最近在研究毫米波雷达模块嘛&#xff0c;期望用在后续的产品中&#xff0c;正好看到瑞萨的活动送板子&#xff0c;手一下没忍住。 拿了板子就得干活咯&#xff0c;我一路火花带闪电&#xff0c;开整…

【灵码助力安全3】——利用通义灵码辅助智能合约漏洞检测的尝试

前言 随着区块链技术的快速发展&#xff0c;智能合约作为去中心化应用&#xff08;DApps&#xff09;的核心组件&#xff0c;其重要性日益凸显。然而&#xff0c;智能合约的安全问题一直是制约区块链技术广泛应用的关键因素之一。由于智能合约代码一旦部署就难以更改&#xf…

腾讯云下架印度云服务器节点,印度云服务器租用何去何从

近日&#xff0c;腾讯云下架印度云服务器节点的消息引起了业界的广泛关注。这一变动让许多依赖印度云服务器的用户开始担忧&#xff0c;印度云服务器租用的未来究竟在何方&#xff1f; 从印度市场本身来看&#xff0c;其云服务市场的潜力不容小觑。据 IDC 报告&#xff0c;到 2…

【RTSP】使用webrtc播放rtsp视频流

一、简介 rtsp流一般是监控、摄像机的实时视频流,现在的主流浏览器是不支持播放rtsp流文件的,所以需要借助其他方案来播放实时视频,下面介绍下我采用的webrtc方案,实测可行。 二、webrtc-streamer是什么? webrtc-streamer是一个使用简单机制通过 WebRTC 流式传输视频捕获…

多并发发短信处理(头条项目-07)

1 pipeline操作 Redis数据库 Redis 的 C/S 架构&#xff1a; 基于客户端-服务端模型以及请求/响应协议的 TCP服务。客户端向服务端发送⼀个查询请求&#xff0c;并监听Socket返回。通常是以 阻塞模式&#xff0c;等待服务端响应。服务端处理命令&#xff0c;并将结果返回给客…

【网络协议】动态路由协议

前言 本文将概述动态路由协议&#xff0c;定义其概念&#xff0c;并了解其与静态路由的区别。同时将讨论动态路由协议相较于静态路由的优势&#xff0c;学习动态路由协议的不同类别以及无类别&#xff08;classless&#xff09;和有类别&#xff08;classful&#xff09;的特性…

c#集成npoi根据excel模板导出excel

NuGet中安装npoi 创建excel模板&#xff0c;替换其中的内容生成新的excel文件。 例子中主要写了这四种情况&#xff1a; 1、替换单个单元格内容&#xff1b; 2、替换横向多个单元格&#xff1b; 3、替换表格&#xff1b; 4、单元格中插入图片&#xff1b; using System.IO; …

人工智能知识分享第十天-机器学习_聚类算法

聚类算法 1 聚类算法简介 1.1 聚类算法介绍 一种典型的无监督学习算法&#xff0c;主要用于将相似的样本自动归到一个类别中。 目的是将数据集中的对象分成多个簇&#xff08;Cluster&#xff09;&#xff0c;使得同一簇内的对象相似度较高&#xff0c;而不同簇之间的对象相…

B树及其Java实现详解

文章目录 B树及其Java实现详解一、引言二、B树的结构与性质1、节点结构2、性质 三、B树的操作1、插入操作1.1、插入过程 2、删除操作2.1、删除过程 3、搜索操作 四、B树的Java实现1、节点类实现2、B树类实现 五、使用示例六、总结 B树及其Java实现详解 一、引言 B树是一种多路…

本地缓存:Guava Cache

这里写目录标题 一、范例二、应用场景三、加载1、CacheLoader2、Callable3、显式插入 四、过期策略1、基于容量的过期策略2、基于时间的过期策略3、基于引用的过期策略 五、显示清除六、移除监听器六、清理什么时候发生七、刷新八、支持更新锁定能力 一、范例 LoadingCache<…

【高录用 | 快见刊 | 快检索】第十届社会科学与经济发展国际学术会议 (ICSSED 2025)

第十届社会科学与经济发展国际学术会议(ICSSED 2025)定于2025年2月28日-3月2日在中国上海隆重举行。会议主要围绕社会科学与经济发展等研究领域展开讨论。会议旨在为从事社会科学与经济发展研究的专家学者提供一个共享科研成果和前沿技术&#xff0c;了解学术发展趋势&#xff…

[ComfyUI]接入Google的Whisk,巨物融合玩法介绍

一、介紹​ 前段时间&#xff0c;谷歌推出了一个图像生成工具whisk&#xff0c;有一个很好玩的图片融合玩法&#xff0c;分别提供三张图片,就可以任何组合来生成图片。​ ​ 最近我发现有人开发了对应的ComfyUI插件&#xff0c;对whisk做了支持&#xff0c;就来体验了下&#…

模式识别与机器学习

文章目录 考试题型零、简介1.自学内容(1)机器学习(2)机器学习和统计学中常见的流程(3)导数 vs 梯度(4)KL散度(5)凸优化问题 2.基本概念3.典型的机器学习系统4.前沿研究方向举例 一、逻辑回归1.线性回归2.逻辑回归3.随堂练习 二、贝叶斯学习基础1.贝叶斯公式2.贝叶斯决策3.分类器…