在SpringBoot项目中使用多线程(配合线程池)加快从MySQL导入数据到ElasticSearch的速度

文章目录

  • 1. 准备工作
    • 1.1 索引库
    • 1.2 建表
    • 1.3 实体类
      • 1.3.1 item.java
      • 1.3.2 itemDocument.java
    • 1.4 编写配置文件
    • 1.5 编写 Mapper 类和 Service 类
  • 2. 没有使用多线程的情况
    • 2.1 编码
    • 2.2 测试结果
  • 3. 使用多线程(配合线程池)的情况
    • 3.1 自定义类,实现 Runnable 接口
    • 3.2 编码(结合线程池)
    • 3.3 测试
  • 4. 对比及分析

1. 准备工作

测试环境:

  • JDK 17.0.7
  • SpringBoot 3.0.2
  • MySQL 8.0.34
  • ElasticSearch 7.17.18

本次测试主要利用的是 Mybatis Plus、PageHelper、fastjson2

MybatisPlus 的 Maven 依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.7</version>
</dependency>

PageHelper 的 Maven 依赖

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>2.1.0</version><exclusions><exclusion><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions>
</dependency>

fastjson2的 Maven 依赖

<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.50</version>
</dependency>

1.1 索引库

创建一个名为 shopping_mall 的索引库

{"mappings": {"properties": {"id": {"type": "keyword"},"name": {"type": "text","analyzer": "ik_smart"},"price": {"type": "integer"},"image": {"type": "keyword","index": false},"category": {"type": "keyword"},"brand": {"type": "keyword"},"sold": {"type": "integer"},"commentCount": {"type": "integer","index": false},"isAD": {"type": "boolean"},"updateTime": {"type": "date"}}}
}

1.2 建表

表名为 item,表结构如下(由于表中有 88476 条数据,无法在这里展示,需要具体的数据可以私聊我获取 SQL 文件)

/*Navicat Premium Data TransferSource Server         : localhostSource Server Type    : MySQLSource Server Version : 80034 (8.0.34)Source Host           : localhost:3306Source Schema         : blogTarget Server Type    : MySQLTarget Server Version : 80034 (8.0.34)File Encoding         : 65001Date: 25/08/2024 01:59:24
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for item
-- ----------------------------
DROP TABLE IF EXISTS `item`;
CREATE TABLE `item`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品id',`name` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT 'SKU名称',`price` int NOT NULL DEFAULT 0 COMMENT '价格(分)',`stock` int UNSIGNED NOT NULL COMMENT '库存数量',`image` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '商品图片',`category` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '类目名称',`brand` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '品牌名称',`spec` varchar(200) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '规格',`sold` int NULL DEFAULT 0 COMMENT '销量',`comment_count` int NULL DEFAULT 0 COMMENT '评论数',`isAD` tinyint(1) NULL DEFAULT 0 COMMENT '是否是推广广告,true/false',`status` int NULL DEFAULT 2 COMMENT '商品状态 1-正常,2-下架,3-删除',`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`creater` bigint NULL DEFAULT NULL COMMENT '创建人',`updater` bigint NULL DEFAULT NULL COMMENT '修改人',PRIMARY KEY (`id`) USING BTREE,INDEX `status`(`status` ASC) USING BTREE,INDEX `updated`(`update_time` ASC) USING BTREE,INDEX `category`(`category` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 100002672305 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '商品表' ROW_FORMAT = COMPACT;SET FOREIGN_KEY_CHECKS = 1;

1.3 实体类

1.3.1 item.java

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;@TableName("item")
public class Item implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 商品id*/@TableId(value = "id", type = IdType.AUTO)private Long id;/*** SKU名称*/private String name;/*** 价格(分)*/private Integer price;/*** 库存数量*/private Integer stock;/*** 商品图片*/private String image;/*** 类目名称*/private String category;/*** 品牌名称*/private String brand;/*** 规格*/private String spec;/*** 销量*/private Integer sold;/*** 评论数*/private Integer commentCount;/*** 是否是推广广告,true/false*/@TableField("isAD")private Boolean isAD;/*** 商品状态 1-正常,2-下架,3-删除*/private Integer status;/*** 创建时间*/private LocalDateTime createTime;/*** 更新时间*/private LocalDateTime updateTime;/*** 创建人*/private Long creater;/*** 修改人*/private Long updater;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getPrice() {return price;}public void setPrice(Integer price) {this.price = price;}public Integer getStock() {return stock;}public void setStock(Integer stock) {this.stock = stock;}public String getImage() {return image;}public void setImage(String image) {this.image = image;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getSpec() {return spec;}public void setSpec(String spec) {this.spec = spec;}public Integer getSold() {return sold;}public void setSold(Integer sold) {this.sold = sold;}public Integer getCommentCount() {return commentCount;}public void setCommentCount(Integer commentCount) {this.commentCount = commentCount;}public Boolean getIsAD() {return isAD;}public void setIsAD(Boolean AD) {isAD = AD;}public Integer getStatus() {return status;}public void setStatus(Integer status) {this.status = status;}public LocalDateTime getCreateTime() {return createTime;}public void setCreateTime(LocalDateTime createTime) {this.createTime = createTime;}public LocalDateTime getUpdateTime() {return updateTime;}public void setUpdateTime(LocalDateTime updateTime) {this.updateTime = updateTime;}public Long getCreater() {return creater;}public void setCreater(Long creater) {this.creater = creater;}public Long getUpdater() {return updater;}public void setUpdater(Long updater) {this.updater = updater;}@Overridepublic String toString() {return "Item{" +"id=" + id +", name='" + name + '\'' +", price=" + price +", stock=" + stock +", image='" + image + '\'' +", category='" + category + '\'' +", brand='" + brand + '\'' +", spec='" + spec + '\'' +", sold=" + sold +", commentCount=" + commentCount +", isAD=" + isAD +", status=" + status +", createTime=" + createTime +", updateTime=" + updateTime +", creater=" + creater +", updater=" + updater +'}';}}

1.3.2 itemDocument.java

import java.time.LocalDateTime;/*** 索引库实体类*/
public class ItemDocument {private Long id;private String name;private Integer price;private Integer stock;private String image;private String category;private String brand;private Integer sold;private Integer commentCount;private Boolean isAD;private LocalDateTime updateTime;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getPrice() {return price;}public void setPrice(Integer price) {this.price = price;}public Integer getStock() {return stock;}public void setStock(Integer stock) {this.stock = stock;}public String getImage() {return image;}public void setImage(String image) {this.image = image;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public Integer getSold() {return sold;}public void setSold(Integer sold) {this.sold = sold;}public Integer getCommentCount() {return commentCount;}public void setCommentCount(Integer commentCount) {this.commentCount = commentCount;}public Boolean getIsAD() {return isAD;}public void setIsAD(Boolean AD) {isAD = AD;}public LocalDateTime getUpdateTime() {return updateTime;}public void setUpdateTime(LocalDateTime updateTime) {this.updateTime = updateTime;}@Overridepublic String toString() {return "ItemDocument{" +"id='" + id + '\'' +", name='" + name + '\'' +", price=" + price +", stock=" + stock +", image='" + image + '\'' +", category='" + category + '\'' +", brand='" + brand + '\'' +", sold=" + sold +", commentCount=" + commentCount +", isAD=" + isAD +", updateTime=" + updateTime +'}';}}

1.4 编写配置文件

编写配置文件前,先导入 MySQL 连接驱动的 Maven 依赖

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency>

application.yaml

spring:datasource:url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: 123456

编写完配置文件后,在项目的启动类上添加 @MapperScan 注解,指定 Mapper 所在的包

@MapperScan("cn.edu.scau.mapper")

1.5 编写 Mapper 类和 Service 类

ItemMapper.java

import cn.edu.scau.pojo.Item;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface ItemMapper extends BaseMapper<Item> {}

ItemService.java

import cn.edu.scau.pojo.Item;
import com.baomidou.mybatisplus.extension.service.IService;public interface ItemService extends IService<Item> {}

ItemServiceImpl.java

import cn.edu.scau.mapper.ItemMapper;
import cn.edu.scau.pojo.Item;
import cn.edu.scau.service.ItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements ItemService {}

完成上述工作后,编写一个测试类,检查 ItemServiceImpl 类能否正常工作

import cn.edu.scau.service.ItemService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class ItemServiceTests {@Autowiredprivate ItemService itemService;@Testpublic void test() {System.out.println(itemService.getById(317578L));}}

2. 没有使用多线程的情况

我们先来测试一下没有使用多线程的情况

2.1 编码

import cn.edu.scau.pojo.Item;
import cn.edu.scau.pojo.ItemDocument;
import cn.edu.scau.service.ItemService;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.xcontent.XContentType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
public class BulkInsertDocumentTests {private RestHighLevelClient restHighLevelClient;@Autowiredprivate ItemService itemService;@Testpublic void testBulkInsertDocument() throws Exception {int pageNumber = 1;int pageSize = 500;while (true) {// 1.准备文档数据QueryWrapper<Item> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(Item::getStatus, 1);PageHelper.startPage(pageNumber, pageSize);List<Item> itemList = itemService.list(queryWrapper);if (itemList == null || itemList.isEmpty()) {return;}// 2.准备 BulkRequest 对象BulkRequest bulkRequest = new BulkRequest();// 3.准备请求参数ItemDocument itemDocument;for (Item item : itemList) {itemDocument = new ItemDocument();BeanUtils.copyProperties(item, itemDocument);bulkRequest.add(new IndexRequest("shopping_mall").id(item.getId().toString()).source(JSON.toJSONString(itemDocument), XContentType.JSON));}// 4.发送请求restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);// 5.翻页pageNumber++;}}@BeforeEachpublic void setUp() {restHighLevelClient = new RestHighLevelClient(RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));}@AfterEachpublic void tearDown() throws Exception {restHighLevelClient.close();}}

2.2 测试结果

GET /shopping_mall/_count

共有 88475 条数据

  1. 第一次导入耗时 36 秒 954 毫秒
  2. 第二次导入耗时 38 秒 454 毫秒
  3. 第三次导入耗时 38 秒 910 毫秒
  4. 第四次导入耗时 40 秒 671毫秒
  5. 第五次导入耗时 38 秒 958毫秒
  6. 第六次导入耗时 38 秒 470毫秒

3. 使用多线程(配合线程池)的情况

3.1 自定义类,实现 Runnable 接口

import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;import java.io.IOException;
import java.util.concurrent.CountDownLatch;public class InsertDocumentThread implements Runnable {private final RestHighLevelClient restHighLevelClient;private final BulkRequest bulkRequest;private final CountDownLatch countDownLatch;public InsertDocumentThread(RestHighLevelClient restHighLevelClient, BulkRequest bulkRequest, CountDownLatch countDownLatch) {this.restHighLevelClient = restHighLevelClient;this.bulkRequest = bulkRequest;this.countDownLatch = countDownLatch;}@Overridepublic void run() {try {restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);countDownLatch.countDown();} catch (IOException e) {throw new RuntimeException(e);}}}

3.2 编码(结合线程池)

@Test
public void testBulkInsertDocumentWithMultipleThread() {int availableProcessors = Runtime.getRuntime().availableProcessors();LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(1000);ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2 * availableProcessors,4 * availableProcessors,100,TimeUnit.SECONDS,linkedBlockingQueue,Thread::new,new ThreadPoolExecutor.DiscardPolicy());int pageNumber = 1;int pageSize = 500;long count = itemService.count() % pageSize == 0 ? itemService.count() / pageSize : itemService.count() / pageSize + 1;CountDownLatch countDownLatch = new CountDownLatch((int) count);long start = System.currentTimeMillis();while (true) {// 1.准备文档数据QueryWrapper<Item> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(Item::getStatus, 1);PageHelper.startPage(pageNumber, pageSize);List<Item> itemList = itemService.list(queryWrapper);if (itemList == null || itemList.isEmpty()) {break;}// 2.准备 BulkRequest 对象BulkRequest bulkRequest = new BulkRequest();// 3.准备请求参数ItemDocument itemDocument;for (Item item : itemList) {itemDocument = new ItemDocument();BeanUtils.copyProperties(item, itemDocument);bulkRequest.add(new IndexRequest("shopping_mall").id(item.getId().toString()).source(JSON.toJSONString(itemDocument), XContentType.JSON));}// 4.发送请求InsertDocumentThread insertDocumentThread = new InsertDocumentThread(restHighLevelClient, bulkRequest, countDownLatch);threadPoolExecutor.submit(insertDocumentThread);// 5.翻页pageNumber++;}try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start) / 1000 + "秒 " + (end - start) % 1000 + " 毫秒");
}

3.3 测试

DELETE /shopping_mall

我们先删除 shopping_mall 索引库,再次进行批量导入操作


GET /shopping_mall/_count

共导入 88475 条数据

  1. 第一次导入耗时 30秒 657 毫秒
  2. 第二次导入耗时 35 秒 200 毫秒
  3. 第三次导入耗时 32 秒 265 毫秒
  4. 第四次导入耗时 34 秒 11 毫秒
  5. 第五次导入耗时 30 秒 778 毫秒
  6. 第六次导入耗时 32 秒 861 毫秒

4. 对比及分析

在这里插入图片描述

通过对比可以发现,使用多线程从 MySQL 批量导入数据到 ElasticSearch,虽然速度提升了一点,但是不多,可能是因为以下原因:

  1. 服务器的 CPU 核心数:我在做测试时,数据库用的是本地的,但 ElasticSearch 用的是云服务器,云服务器的 CPU 配置是 2 核,这也可能是导致使用多线程批量导入数据速度提升不明显的原因
  2. I/O 密集型操作:Elasticsearch 的索引操作通常是 I/O 密集型的,这意味着瓶颈可能在于网络延迟和 Elasticsearch 服务器的响应时间,而不是 CPU 的处理能力,在这种情况下,增加线程数可能不会显著提高性能,因为 I/O 操作无法并行执行得更快
  3. 网络带宽限制:网络带宽可能是瓶颈(我使用的云服务器的带宽是 6M),特别是在批量插入大量数据时

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

python与pytroch相关

1.pytroch模型类 PyTorch 是一个易学且清晰明了的深度学习库。本节讲解如何查看一个模型的结构。 首先&#xff0c;最简单创建模型的方式如下&#xff1a; #导入必要的库 import torch.nn as nn myNetnn.Sequential(nn.Linear(2,10),#第一层&#xff08;全连接层&#xff09;&…

【苍穹外卖】Day 5 Redis、店铺营业状态接口

1 基本介绍 Redis是一个基于 内存 的 key-value 结构数据库 基于内存存储&#xff0c;读写性能高适合存储热点数据(热点商品、资讯、新闻)企业应用广泛 运行 在cmd下 redis-server.exe redis.windows.conf 启动状态下&#xff0c;再 redis-cli.exe 测试&#xff1a; 也可以…

继承 继承 继承 屁

1.栈 #include <iostream>using namespace std; class Stack {int *ptr;int size; public://无参构造Stack():size(-1){ptrnew int[8];cout<<"无参构造"<<endl;}//有参构造Stack(int a):size(0){ptrnew int[8];ptr[0]a;cout<<"有参构造…

Phalcon 增删改查的搭建过程

一 结果展示 先展示效果: 1 查询: 2 删除 3 插入 插入之前,数据库里面表的数据如下: 插入之后:

性能优化:自动化处理系统设计

性能优化&#xff1a;自动化处理系统设计 前言需求分析系统设计1. 调度中心2. 任务执行器3. 错误处理机制4. 通知系统5. 报表生成器6. 日志记录器 技术实现结语 前言 在当今这个信息爆炸、技术日新月异的时代&#xff0c;企业面临着前所未有的挑战和机遇。随着业务量的不断增长…

第十九篇——行军篇:侦察兵的32条行军法则

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 微观层面也有很多值得去注意&#xff0c;刻意练习的地方&#xff1b;看似…

深度探索Unity与C#:编织游戏世界的奇幻篇章

在数字编织的梦幻之境中&#xff0c;Unity游戏引擎与C#编程语言如同双生子&#xff0c;共同编织着游戏世界的奇幻篇章。《Unity游戏开发实战&#xff1a;从零到C#高手》这本书&#xff0c;不仅仅是技术的堆砌&#xff0c;它更像是一位智慧导师&#xff0c;引领着我们深入探索这…

html+css+js网页设计 专业知识 珠宝历史10个页面

htmlcssjs网页设计 专业知识 珠宝历史10个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 …

【算法】贪心算法解析:基本概念、策略证明与代码例题演示

文章目录 1. 什么是贪心算法&#xff1f;2. 贪心算法的特点3. 例题&#xff08;贪心策略&#xff09;① 找零问题② 最小路径和③ 背包问题 4. 贪心策略证明 1. 什么是贪心算法&#xff1f; 在学习贪心算法之前&#xff0c;一定要理解的是贪心策略&#xff1a; 贪心策略是一种…

美畅物联丨科技赋能校车安全:智慧监控管理系统的创新应用

1、背景 1.1应用需求 孩子&#xff0c;作为国家未来的希望之星和民族发展的潜力所在&#xff0c;其安全与健康向来都是社会瞩目的核心要点。校车&#xff0c;作为孩子们日常出行的关键交通载体&#xff0c;其安全性更是时刻牵动着每一个家庭的敏感神经。然而&#xff0c;不可…

JavaSwing项目ATM自动提款机(mysql数据库)+详细报告

目 录 第一章 引言... 1 1.1 设计目的... 1 1.2 相关开发工具介绍... 1 第二章 数据库需求分析... 2 2.1 系统功能分析... 2 2.2 功能模块设计... 2 第三章 数据库概念结构设计... 3 3.1 概念模型... 3 3.2 E-R图... 3 第四章 数据库逻辑结构设计... 4 4.1 关系模型设计... 4 …

Allure报告下载不同格式的文件

支持类型&#xff1a; class AttachmentType(Enum):def __init__(self, mime_type, extension):self.mime_type mime_typeself.extension extensionTEXT ("text/plain", "txt")CSV ("text/csv", "csv")TSV ("text/tab-sep…

【Datawhale X 李宏毅苹果书 AI夏令营】《深度学习详解》Task3 打卡

文章目录 前言学习目标一、优化策略二、模型偏差三、优化问题三、过拟合增加训练集给模型一些限制 四、交叉验证五、不匹配总结 前言 本文是【Datawhale X 李宏毅苹果书 AI夏令营】的Task3学习笔记打卡。 学习目标 李宏毅老师对应视频课程&#xff1a;https://www.bilibili.…

深度强化学习算法(六)(附带MATLAB程序)

深度强化学习&#xff08;Deep Reinforcement Learning, DRL&#xff09;结合了深度学习和强化学习的优点&#xff0c;能够处理具有高维状态和动作空间的复杂任务。它的核心思想是利用深度神经网络来逼近强化学习中的策略函数和价值函数&#xff0c;从而提高学习能力和决策效率…

js控制滚轮横向滚动

获取元素&#xff0c;使用一下方法 let box document.getElementById("table_box");box.addEventListener("wheel", function (e) {//这里使用的是 chrom浏览器测试的,有一些Api不太准确 ,请大家注意!!!!let left -e.wheelDelta || e.deltaY / 2;box.sc…

JavaScript中console.log()拼接用逗号和加号的区别

JavaScript中console.log()拼接用逗号和加号的区别 在JavaScript中&#xff0c;console.log()方法可以使用加号&#xff08;&#xff09;或逗号&#xff08;&#xff0c;&#xff09;来拼接字符串。 使用加号&#xff08;&#xff09;时&#xff0c;将两个字符串连接起来&…

多参数水质分析仪

多参数水质分析仪是一种能够同时测量并分析多种水质参数的仪器。其主要功能包括&#xff1a; 测量多种水质参数&#xff1a;多参数水质分析仪可以同时测量多种水质指标&#xff0c;例如pH值、电导率、溶解氧&#xff08;DO&#xff09;、浑浊度、温度等。 高精度测量&#xff…

Android解析异步消息处理机制

文章目录 Android解析异步消息处理机制MessageHandlerMessageQueueLooper Android解析异步消息处理机制 Android中的异步消息处理主要由4个部分组成&#xff1a;Message、Handler、MessageQueue和Looper。其中Message和Handler在上一小节中我们已经接触过了&#xff0c;而Mess…

C语言 | Leetcode C语言题解之第388题文件的最长绝对路径

题目&#xff1a; 题解&#xff1a; #define MAX(a, b) ((a) > (b) ? (a) : (b))int lengthLongestPath(char * input){int n strlen(input);int pos 0;int ans 0;int * level (int *)malloc(sizeof(int) * (n 1));memset(level, 0, sizeof(int) * (n 1));while (po…

第L3周:机器学习-逻辑回归

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 逻辑回归适用于分类问题&#xff0c;主要用于解决二分类或多分类的问题。比如&#xff1a;用户购买某商品的可能性&#xff0c;某病人患有某…