【存储中间件API】MySQL、Redis、MongoDB、ES常见api操作及性能比较

常见中间件api操作及性能比较

  • ☝️ MySQL crud操作
    • ✌️ maven依赖
    • ✌️ 配置
    • ✌️ 定义实体类
    • ✌️ 常用api
  • ☝️ Redis crud操作
    • ✌️ maven依赖
    • ✌️ 配置
    • ✌️ 常用api
  • ☝️ MongoDB crud操作
    • ✌️ maven依赖
    • ✌️ 配置文件
    • ✌️ 定义实体类
    • ✌️ MongoDB常用api
  • ☝️ ES crud操作 ⭐️⭐️⭐️
    • ✌️ 前期准备
    • ✌️ maven依赖
      • ⭐️ tips
    • ✌️ 配置文件
    • ✌️ 定义实体类
    • ✌️ ES常用api
  • ☝️ 性能比较
    • ✌️ 模拟创建数据接口
    • ✌️ 查询数据接口

本文汇总常见中间件的api操作及性能对比,主要涉及MySQL、Redis、Mongo、Es,这篇文章默认已经安装配置好相应的中间件

关于MongoDB的安装配置可参考文章:《【MongoDB】一问带你深入理解什么是MongDB,MongoDB超超详细保姆级教程》

Es安装配置可参考:《【ELK】window下ELK的安装与部署》

☝️ MySQL crud操作

mysql是目前最常用的关系型数据库,网上有很多资料,这里大致简单过一下主流的Mybatis-Plus用法,不展开细说

✌️ maven依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId>
</dependency><!--添加 Alibaba 数据源-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.20</version>
</dependency>
<!--访问mysql-->
<!--JDBC-->
<!-- MySql 5.5 Connector -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.24</version>
</dependency>

✌️ 配置

ip、port、数据库名称,账户密码换成自己的

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/csdn?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=GMT+8username: rootpassword: 123456

✌️ 定义实体类

实体类中,@Data、@EqualsAndHashCode、@Accessorslombok注解,@Data自动生成实体类的Getter、Setter、无参构造、有参构造等,@EqualsAndHashCode生成自动生成 equalshashCode 方法,@Accessors主要作用是支持链式调用,@TableName是MP注解,用于映射表名

@Data
@EqualsAndHashCode
@Accessors(chain = true)
@TableName("t_store_product")
public class StoreProduct implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String image;private String sliderImage;private String storeName;private String storeInfo;private String keyword;private String cateId;private String unitName;private Integer sort;private Boolean isHot;private Boolean isBenefit;private Boolean isBest;private Boolean isNew;private Boolean isGood;private Integer giveIntegral;private Boolean isSub;private Integer ficti;private Integer tempId;private Boolean specType;private String activity;private String attr;private String attrValue;private String content;private String couponIds;private String flatPattern;
}

✌️ 常用api

见文件TestMySQL.java

@Slf4j
@SpringBootTest
public class TestMySQL {@Resourceprivate StoreProductMapper storeProductMapper;/*** @param num 生成num条模拟数据* @return*/private static List<StoreProduct> getStoreProduct(Integer num) {List<StoreProduct> result = new ArrayList<>();StoreProduct storeProduct = new StoreProduct();for (int i = 0; i < num; i++) {storeProduct.setId(999 + i).setImage("https://www.baidu.com/img/bd_logo1.png").setSliderImage("https://www.baidu.com/img/bd_logo1.png").setStoreName("测试商品" + i).setStoreInfo("测试商品").setKeyword("测试商品").setCateId("1").setUnitName("件").setSort(1).setIsHot(true).setIsBenefit(true).setIsBest(true).setIsNew(true).setIsGood(true).setGiveIntegral(1).setIsSub(true).setFicti(1).setTempId(1).setSpecType(true).setActivity("{\"test\":\"test\"}").setAttr("{\"test\":\"test\"}").setAttrValue("{\"test\":\"test\"}").setContent("{\"test\":\"test\"}").setCouponIds("{\"test\":\"test\"}").setFlatPattern("{\"test\":\"test\"}");result.add(storeProduct);}return result;}/*** 插入单条数据*/@Testvoid test_insert() {StoreProduct storeProduct = getStoreProduct(1).get(0);storeProductMapper.insert(storeProduct);}/*** 按照id删除*/@Testvoid test_deleteById() {storeProductMapper.deleteById(999);}/*** 多个条件删除*/@Testvoid test_deleteByMap() {Map<String, Object> columnMap = new HashMap<>();// 添加多个条件columnMap.put("id", 999);columnMap.put("store_name", "测试商品");columnMap.put("is_hot", true);storeProductMapper.deleteByMap(columnMap);}/*** 构建wrapper语句删除*/@Testvoid test_deleteByWrapper() {// 创建 QueryWrapper 对象QueryWrapper<StoreProduct> queryWrapper = new QueryWrapper<>();// 添加删除条件,例如删除 id 为 "999" 的记录,并且storeName 为 "测试商品" 的记录queryWrapper.eq("id", 999);storeProductMapper.delete(queryWrapper);}/*** 构建wrapper语句删除*/@Testvoid test_deleteByLambdaWrapper() {// 创建 LambdaQueryWrapper 对象LambdaQueryWrapper<StoreProduct> queryWrapper = new LambdaQueryWrapper<>();// 添加删除条件,例如删除 id 为 "999" 的记录,并且storeName 为 "测试商品" 的记录queryWrapper.eq(StoreProduct::getId, 999);storeProductMapper.delete(queryWrapper);}/*** 批量删除*/@Testvoid test_deleteBatchIds() {storeProductMapper.deleteBatchIds(Arrays.asList(999, 1000));}/*** 更新数据*/@Testvoid test_updateById() {StoreProduct storeProduct = getStoreProduct(1).get(0);storeProduct.setStoreName("商品名字更新啦~");storeProductMapper.updateById(storeProduct);}/*** 构建wrapper语句更新*/@Testvoid test_updateByWrapper() {// 创建 UpdateWrapper 对象UpdateWrapper<StoreProduct> queryWrapper = new UpdateWrapper<>();// 添加更新条件,例如更新 id 为 "999"queryWrapper.eq("id", 999);queryWrapper.set("store_name", "商品名字再次更新啦~");storeProductMapper.update(null, queryWrapper);}/*** 构建LambdaWrapper语句更新*/@Testvoid test_updateByLambdaWrapper() {// 创建 UpdateWrapper 对象LambdaUpdateWrapper<StoreProduct> queryWrapper = new LambdaUpdateWrapper<>();// 添加更新条件,例如更新 id 为 "999"queryWrapper.eq(StoreProduct::getId, 999);queryWrapper.set(StoreProduct::getStoreName, "商品名字再再次更新啦~");storeProductMapper.update(null, queryWrapper);}/*** 通过id查找*/@Testvoid test_selectById() {StoreProduct storeProduct = storeProductMapper.selectById(999);log.info("查询结果:{}", storeProduct);}/*** 通过id集合查找*/@Testvoid test_selectBatchIds() {List<StoreProduct> storeProducts = storeProductMapper.selectBatchIds(Arrays.asList(1, 2));for (StoreProduct storeProduct : storeProducts) {log.info("查询结果:{}", storeProduct);}}/*** 通过map查找*/@Testvoid test_selectByMap() {Map<String, Object> columnMap = new HashMap<>();// 添加多个条件columnMap.put("store_info", "测试商品");columnMap.put("is_hot", true);List<StoreProduct> storeProducts = storeProductMapper.selectByMap(columnMap);for (StoreProduct storeProduct : storeProducts) {log.info("查询结果:{}", storeProduct);}}/*** 根据条件查一个** 注意,如果有多个满足条件的数据,代码会报错:One record is expected, but the query result is multiple records*/@Testvoid test_selectOne() {QueryWrapper<StoreProduct> queryWrapper = new QueryWrapper<>();queryWrapper.eq("id", 999);StoreProduct storeProduct = storeProductMapper.selectOne(queryWrapper);log.info("查询结果:{}", storeProduct);}/*** 按照条件查询count总数*/@Testvoid test_selectCount() {QueryWrapper<StoreProduct> queryWrapper = new QueryWrapper<>();queryWrapper.eq("store_name", "新款智能手机");Long count = storeProductMapper.selectCount(queryWrapper);log.info("查询结果有:{} 条", count);}/*** 列表查询*/@Testvoid test_selectList() {QueryWrapper<StoreProduct> queryWrapper = new QueryWrapper<>();queryWrapper.eq("store_name", "新款智能手机");List<StoreProduct> storeProducts = storeProductMapper.selectList(queryWrapper);for (StoreProduct storeProduct : storeProducts) {log.info("查询结果:{}", storeProduct);}}/*** 查询结果为map*/@Testvoid test_selectMaps() {QueryWrapper<StoreProduct> queryWrapper = new QueryWrapper<>();queryWrapper.eq("store_info", "测试商品");List<Map<String, Object>> maps = storeProductMapper.selectMaps(queryWrapper);for (Map<String, Object> map : maps) {log.info("查询结果:{}", map);}}/*** 分页查询*/@Testvoid test_selectPage() {// 创建分页对象,指定当前页码和每页记录数LambdaQueryWrapper<StoreProduct> lqw = new LambdaQueryWrapper<>();lqw.eq(StoreProduct::getStoreName, "新款智能手机");Page<StoreProduct> page = new Page<>(1, 10);// 调用 selectPage 方法进行分页查询IPage<StoreProduct> resultPage = storeProductMapper.selectPage(page, lqw);log.info("当前页码:{},每页记录数:{},总页数:{},总记录数:{}", resultPage.getCurrent(), resultPage.getSize(),resultPage.getPages(), resultPage.getTotal());for (StoreProduct storeProduct : resultPage.getRecords()) {log.info("查询结果:{}", storeProduct);}}
}

代码中构建模拟数据方法getStoreProduct()中用到了链式构建,Wrapper构建既能用普通Wrapper,也能用LambdaWrapper,例如查询中的QueryWrapper()或者是LambdaQueryWrapper()test_selectOne如果查询存在多个值,会抛出异常One record is expected, but the query result is multiple records,源码还是非常简单,如下:

在这里插入图片描述

☝️ Redis crud操作

Redis是常见的缓存中间件,接下来看看redis的一些常见操作

✌️ maven依赖

<!-- Spring Boot Redis 依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.2.0.RELEASE</version>
</dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.1.0</version>
</dependency>

✌️ 配置

ip、port、database,密码换成自己的

spring:redis:host: 127.0.0.1 #地址port: 6379 #端口password:timeout: 30000 # 连接超时时间(毫秒)database: 15  #默认数据库jedis:pool:max-active: 200 # 连接池最大连接数(使用负值表示没有限制)max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)max-idle: 10 # 连接池中的最大空闲连接min-idle: 0 # 连接池中的最小空闲连接time-between-eviction-runs: -1 #逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1

✌️ 常用api

TestRedis.java

@Slf4j
@SpringBootTest
public class TestRedis {@Resource(name = "stringRedisTemplate")private StringRedisTemplate stringRedisTemplate;/*** 测试设置单个键值对*/@Testvoid testSetValue() {stringRedisTemplate.opsForValue().set("testKey", "testValue");String value = stringRedisTemplate.opsForValue().get("testKey");log.info("设置并获取单个键值对,值为: {}", value);}/*** 测试设置带有过期时间的键值对  10秒过期*/@Testvoid testSetValueWithExpiration() {stringRedisTemplate.opsForValue().set("expiringKey", "expiringValue", 10, TimeUnit.SECONDS);String value = stringRedisTemplate.opsForValue().get("expiringKey");log.info("设置带有过期时间的键值对,值为: {}", value);try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}value = stringRedisTemplate.opsForValue().get("expiringKey");log.info("过期时间已到,键值对已过期,值为: {}", value);}/*** 测试获取单个键的值*/@Testvoid testGetValue() {stringRedisTemplate.opsForValue().set("existingKey", "existingValue");String value = stringRedisTemplate.opsForValue().get("existingKey");log.info("获取单个键的值,值为: {}", value);}/*** 测试删除单个键*/@Testvoid testDeleteKey() {stringRedisTemplate.opsForValue().set("toDeleteKey", "toDeleteValue");Boolean result = stringRedisTemplate.delete("toDeleteKey");log.info("删除单个键,结果: {}", result);}/*** 测试批量删除键*/@Testvoid testDeleteKeys() {stringRedisTemplate.opsForValue().set("key1", "value1");stringRedisTemplate.opsForValue().set("key2", "value2");Long deletedCount = stringRedisTemplate.delete(Arrays.asList("key1", "key2"));log.info("批量删除键,删除数量: {}", deletedCount);}/*** 测试设置哈希表*/@Testvoid testSetHash() {Map<String, String> hash = new HashMap<>();hash.put("field1", "value1");hash.put("field2", "value2");stringRedisTemplate.opsForHash().putAll("testHash", hash);Map<Object, Object> result = stringRedisTemplate.opsForHash().entries("testHash");log.info("设置哈希表,结果: {}", result);}/*** 测试获取哈希表中的单个字段值*/@Testvoid testGetHashField() {stringRedisTemplate.opsForHash().put("testHash", "field1", "value1");Object value = stringRedisTemplate.opsForHash().get("testHash", "field1");log.info("获取哈希表中的单个字段值,值为: {}", value);}/*** 测试获取哈希表的所有字段和值*/@Testvoid testGetAllHashFields() {Map<String, String> hash = new HashMap<>();hash.put("field1", "value1");hash.put("field2", "value2");stringRedisTemplate.opsForHash().putAll("testHash", hash);Map<Object, Object> result = stringRedisTemplate.opsForHash().entries("testHash");log.info("获取哈希表的所有字段和值,结果: {}", result);}/*** 测试向列表左侧插入元素*/@Testvoid testLeftPushToList() {stringRedisTemplate.opsForList().leftPush("testList", "element1");stringRedisTemplate.opsForList().leftPush("testList", "element2");List<String> list = stringRedisTemplate.opsForList().range("testList", 0, -1);log.info("向列表左侧插入元素,列表内容: {}", list);}/*** 测试从列表右侧弹出元素*/@Testvoid testRightPopFromList() {stringRedisTemplate.opsForList().leftPush("testList", "element1");stringRedisTemplate.opsForList().leftPush("testList", "element2");String poppedElement = stringRedisTemplate.opsForList().rightPop("testList");log.info("从列表右侧弹出元素,弹出元素: {}", poppedElement);}/*** 测试向集合中添加元素*/@Testvoid testAddToSet() {stringRedisTemplate.opsForSet().add("testSet", "element1", "element2");Set<String> set = stringRedisTemplate.opsForSet().members("testSet");log.info("向集合中添加元素,集合内容: {}", set);}/*** 测试从集合中移除元素*/@Testvoid testRemoveFromSet() {stringRedisTemplate.opsForSet().add("testSet", "element1", "element2");Long removedCount = stringRedisTemplate.opsForSet().remove("testSet", "element1");log.info("从集合中移除元素,移除数量: {}", removedCount);}/*** 测试向有序集合中添加元素*/@Testvoid testAddToZSet() {stringRedisTemplate.opsForZSet().add("testZSet", "element1", 1.0);stringRedisTemplate.opsForZSet().add("testZSet", "element2", 2.0);Set<String> zSet = stringRedisTemplate.opsForZSet().range("testZSet", 0, -1);log.info("向有序集合中添加元素,有序集合内容: {}", zSet);}/*** 测试从有序集合中移除元素*/@Testvoid testRemoveFromZSet() {stringRedisTemplate.opsForZSet().add("testZSet", "element1", 1.0);stringRedisTemplate.opsForZSet().add("testZSet", "element2", 2.0);Long removedCount = stringRedisTemplate.opsForZSet().remove("testZSet", "element1");log.info("从有序集合中移除元素,移除数量: {}", removedCount);}
}

运行结果:

在这里插入图片描述

☝️ MongoDB crud操作

MongoDB是目前常用的高性能的分布式文件存储方案,下面看看他的api实现

✌️ maven依赖

<!-- mongodb连接驱动 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

✌️ 配置文件

我这里图省事,直接在config中写死了mongodb://127.0.0.1:27017/csdn,可配置在yml文件中读取

@Configuration
public class MongoConfig {@Beanpublic MongoDatabaseFactory mongoDatabaseFactory() {String connectionString = "mongodb://127.0.0.1:27017/csdn";return new SimpleMongoClientDatabaseFactory(connectionString);}@Bean(name = "mongoTemplate")public MongoTemplate mongoTemplate() {return new MongoTemplate(mongoDatabaseFactory());}
}

✌️ 定义实体类

代码中,@Data还是lombok注解,和mysql一样,@Document注解可以理解成映射行

如下图中:
在这里插入图片描述

图中的Mongo的集合(Collection)类比MySQL中的表名,Document类比表中的一行

那为什么一行在navicat中显示有那么多条数据呢?其实Mongo底层是BSON(Binary JSON)二进制存储格式,每个Document下面是一个大的json文件,样例如下:

{"_id": "order123","orderDate": "2025-02-18","customer": {"customerId": "cust456","name": "John Doe","email": "john.doe@example.com"},"items": [{"productId": "prod789","productName": "Smartphone","quantity": 2,"price": 500},{"productId": "prod012","productName": "Headphones","quantity": 1,"price": 100}]
}

@Id注解可以理解成主键,一个对象中只能有一个,可以生动赋值,也可以用默认值,默认值按照ObjectId来取值,包含了时间戳、机器标识、进程 ID 和随机数等信息

在这里插入图片描述

MongoStoreProduct.java

@Data
@Document("storeproductinfo")
public class MongoStoreProduct {/*** 文档的id使用ObjectId类型来封装,并且贴上@Id注解*/@Id@Field("_id")@JsonProperty("_id")private String id;/*** 图片*/@Field("image")@JsonProperty("image")private String image;/*** 轮播图片*/@Field("sliderImage")@JsonProperty("sliderImage")private String sliderImage;/*** 店铺名称*/@Field("storeName")@JsonProperty("storeName")private String storeName;/*** 店铺信息*/@Field("storeInfo")@JsonProperty("storeInfo")private String storeInfo;/*** 关键词*/@Field("keyword")@JsonProperty("keyword")private String keyword;/*** 分类ID*/@Field("cateId")@JsonProperty("cateId")private String cateId;/*** 单位名称*/@Field("unitName")@JsonProperty("unitName")private String unitName;/*** 排序*/@Field("sort")@JsonProperty("sort")private Integer sort;/*** 是否热门*/@Field("isHot")@JsonProperty("isHot")private Boolean isHot;/*** 是否有优惠*/@Field("isBenefit")@JsonProperty("isBenefit")private Boolean isBenefit;/*** 是否精品*/@Field("isBest")@JsonProperty("isBest")private Boolean isBest;/*** 是否新品*/@Field("isNew")@JsonProperty("isNew")private Boolean isNew;/*** 是否好评*/@Field("isGood")@JsonProperty("isGood")private Boolean isGood;/*** 赠送积分*/@Field("giveIntegral")@JsonProperty("giveIntegral")private Integer giveIntegral;/*** 是否子店铺*/@Field("isSub")@JsonProperty("isSub")private Boolean isSub;/*** 虚拟销量*/@Field("ficti")@JsonProperty("ficti")private Integer ficti;/*** 模板ID*/@Field("tempId")@JsonProperty("tempId")private Integer tempId;/*** 规格类型*/@Field("specType")@JsonProperty("specType")private Boolean specType;/*** 活动*/@Field("activity")@JsonProperty("activity")private String activity;/*** 属性*/@Field("attr")@JsonProperty("attr")private String attr;/*** 属性值*/@Field("attrValue")@JsonProperty("attrValue")private String attrValue;/*** 内容*/@Field("content")@JsonProperty("content")private String content;/*** 优惠券ID列表*/@Field("couponIds")@JsonProperty("couponIds")private String couponIds;/*** 平铺模式*/@Field("flatPattern")@JsonProperty("flatPattern")private String flatPattern;
}

✌️ MongoDB常用api

TestMongoDB.java

@Slf4j
@SpringBootTest
public class TestMongoDB {@Resourceprivate StoreProductMongoRepository storeProductMongoRepository;/*** 生成模拟数据** @param num 生成的数量* @return 模拟数据列表*/private List<MongoStoreProduct> getStoreProduct(Integer num) {List<MongoStoreProduct> result = new ArrayList<>();for (int i = 0; i < num; i++) {MongoStoreProduct mongoStoreProduct = new MongoStoreProduct();mongoStoreProduct.setId(String.valueOf(999 + i)).setImage("https://www.baidu.com/img/bd_logo1.png").setSliderImage("https://www.baidu.com/img/bd_logo1.png").setStoreName("测试商品" + i).setStoreInfo("测试商品" + i).setKeyword("测试商品").setCateId("1").setUnitName("件").setSort(1).setIsHot(true).setIsBenefit(true).setIsBest(true).setIsNew(true).setIsGood(true).setGiveIntegral(1).setIsSub(true).setFicti(1).setTempId(1).setSpecType(true).setActivity("{\"test\":\"test\"}").setAttr("{\"test\":\"test\"}").setAttrValue("{\"test\":\"test\"}").setContent("{\"test\":\"test\"}").setCouponIds("{\"test\":\"test\"}").setFlatPattern("{\"test\":\"test\"}");result.add(mongoStoreProduct);}return result;}/*** 插入单条数据  id相同时,内容会进行覆盖*/@Testvoid test_insert() {MongoStoreProduct mongoStoreProduct = getStoreProduct(1).get(0);MongoStoreProduct save = storeProductMongoRepository.save(mongoStoreProduct);log.info("插入单条数据,结果: {}", save);}/*** 插入多条数据  id相同时,内容会进行覆盖*/@Testvoid test_insertMultiple() {List<MongoStoreProduct> storeProduct = getStoreProduct(3);List<MongoStoreProduct> mongoStoreProducts = storeProductMongoRepository.saveAll(storeProduct);mongoStoreProducts.forEach(product -> log.info("插入多条数据,结果: {}", product));}/*** 根据 ID 查询单条数据*/@Testvoid test_findById() {Optional<MongoStoreProduct> mongoStoreProductOpt = storeProductMongoRepository.findById(String.valueOf(999));if (mongoStoreProductOpt.isPresent()) {log.info("根据 ID 查询单条数据,结果: {}", mongoStoreProductOpt.get());} else {log.info("未找到对应 ID 的数据");}}/*** 查询所有数据*/@Testvoid test_findAll() {Iterable<MongoStoreProduct> mongoStoreProducts = storeProductMongoRepository.findAll();mongoStoreProducts.forEach(product -> log.info("查询所有数据,结果: {}", product));}/*** 根据 ID 删除单条数据*/@Testvoid test_deleteById() {storeProductMongoRepository.deleteById(String.valueOf(999));log.info("根据 ID 删除单条数据,删除完成");}/*** 删除所有数据*/@Testvoid test_deleteAll() {storeProductMongoRepository.deleteAll();log.info("删除所有数据,删除完成");}/*** 分页查询数据*/@Testvoid test_findAllByPage() {PageRequest pageRequest = PageRequest.of(0, 2, Sort.by(Sort.Direction.ASC, "id"));Page<MongoStoreProduct> productPage  = storeProductMongoRepository.findAll(pageRequest);log.info("当前页码: {}, 每页记录数: {}, 总记录数: {}, 总页数: {}",productPage.getNumber(), productPage.getSize(), productPage.getTotalElements(), productPage.getTotalPages());// 当前页码: 0, 每页记录数: 2, 总记录数: 23, 总页数: 12productPage.getContent().forEach(product -> log.info("分页查询数据,结果: {}", product));}
}

分页返回结果:

在这里插入图片描述

注意点:

  1. mongoDB在save或者saveAll时,如果id已经存在,则会对改id数据进行覆盖
  2. storeProductMongoRepository中没有更新相关的接口,可以根据第一点特性进行数据覆盖,代码如下,
@Test
void test_updateByFindAndSave() {Optional<StoreProduct> productOptional = storeProductMongoRepository.findById(999L);if (productOptional.isPresent()) {StoreProduct product = productOptional.get();product.setStoreName("更新后的测试商品");StoreProduct updatedProduct = storeProductMongoRepository.save(product);log.info("更新数据,结果: {}", updatedProduct);} else {log.info("未找到对应 ID 的数据,无法更新");}
}

☝️ ES crud操作 ⭐️⭐️⭐️

✌️ 前期准备

MySQL、Redis、MongoDB可视化工具用Navicat可以解决,ES可以使用ElasticHD,当然也可以使用Postman直接查询结果,先简单介绍一下ElasticHD使用

  1. 下载地址:https://github.com/360EntSecGroup-Skylar/ElasticHD/releases
  2. 执行:直接双击ElasticHD.exe。//或./ElasticHD -p 127.0.0.1:980
  3. 启动访问:http://localhost:9800

这个 Dashboard的UI设计非常酷炫:

在这里插入图片描述
输入es连接,点connect登录:

在这里插入图片描述

如果有账号密码,使用

http://username:password@host:port

例如:

http://elastic:elastic@http://127.0.0.1:9200

数据搜索直观易使用:

在这里插入图片描述

索引列表看得比较清楚:

在这里插入图片描述
这个 SQL查询语句ESJson查询格式的小工具挺厉害的:在这里插入图片描述

✌️ maven依赖

⭐️ tips

es 8.x以上版本,只支持springboot 2.7.x以上,且maven依赖的版本需要和es服务的版本要保持一致,因为我的springBoot版本为2.4.2,我的es选取的是7.13.2版本

在这里插入图片描述

<!-- 引入es -->
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-elasticsearch</artifactId><version>4.2.9</version><scope>compile</scope><exclusions><exclusion><groupId>transport</groupId><artifactId>org.elasticsearch.client</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.13.2</version>
</dependency>
<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.13.2</version>
</dependency>

✌️ 配置文件

application.yml

spring:elasticsearch:rest:uris: 127.0.0.1:9200username:password:read-timeout: 120s
es:storeProduct:indexName: store_product_info_v2pageSize: 500
@Configuration
@ConfigurationProperties(prefix = "spring.elasticsearch.rest")
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {@Value("${spring.elasticsearch.rest.uris}")private String uris;@Value("${spring.elasticsearch.rest.username}")private String username;@Value("${spring.elasticsearch.rest.password}")private String password;@Override@Bean(name = "elasticsearchClient", destroyMethod = "close")public RestHighLevelClient elasticsearchClient() {ClientConfiguration configuration = ClientConfiguration.builder().connectedTo(uris).withBasicAuth(username, password).withConnectTimeout(Duration.ofSeconds(60)).withSocketTimeout(Duration.ofSeconds(60)).withHttpClientConfigurer(httpClientBuilder -> httpClientBuilder.setDefaultIOReactorConfig(IOReactorConfig.custom().setSoKeepAlive(true).build()).setKeepAliveStrategy((httpResponse, httpContext) -> 1000 * 60 * 3)).build();return RestClients.create(configuration).rest();}@Override@Bean(name = {"elasticsearchRestTemplate"})public ElasticsearchRestTemplate elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,@Qualifier("elasticsearchClient") RestHighLevelClient elasticsearchClient) {return new ElasticsearchRestTemplate(elasticsearchClient, elasticsearchConverter);}
}

✌️ 定义实体类

@Document(indexName = "store_product_info_v2")注解绑定是es的索引,@Setting是配置文件的目录,number_of_shards是分片数,number_of_replicas表示分片副本数,max_result_window允许搜索最大值

{"index": {"number_of_shards": 1,"number_of_replicas": 1,"max_result_window": 100000}
}

StoreProductEsDTO.java

@Data
@Document(indexName = "store_product_info_v2")
//@Document(indexName = "#{@StoreProductServiceImpl.getStoreProductEsIndexName()}")
@Setting(settingPath = "es/StoreProductSettings.json")
public class StoreProductEsDTO {/*** id*/@Id@Field(type = FieldType.Keyword)private String id;/*** 图片*/@Field(value = "image", type = FieldType.Text)private String image;/*** 滑块图片*/@Field(value = "slider_image", type = FieldType.Text)private String sliderImage;/*** 店铺名称*/@Field(value = "store_name", type = FieldType.Text)private String storeName;/*** 店铺信息*/@Field(value = "store_info", type = FieldType.Text)private String storeInfo;/*** 关键词*/@Field(value = "keyword", type = FieldType.Text)private String keyword;/*** 分类 ID*/@Field(value = "cate_id", type = FieldType.Keyword)private String cateId;/*** 单位名称*/@Field(value = "unit_name", type = FieldType.Text)private String unitName;/*** 排序*/@Field(value = "sort", type = FieldType.Integer)private Integer sort;/*** 是否热门*/@Field(value = "is_hot", type = FieldType.Boolean)private Boolean isHot;/*** 是否有优惠*/@Field(value = "is_benefit", type = FieldType.Boolean)private Boolean isBenefit;/*** 是否精品*/@Field(value = "is_best", type = FieldType.Boolean)private Boolean isBest;/*** 是否新品*/@Field(value = "is_new", type = FieldType.Boolean)private Boolean isNew;/*** 是否优质*/@Field(value = "is_good", type = FieldType.Boolean)private Boolean isGood;/*** 赠送积分*/@Field(value = "give_integral", type = FieldType.Integer)private Integer giveIntegral;/*** 是否子项*/@Field(value = "is_sub", type = FieldType.Boolean)private Boolean isSub;/*** 虚拟数据*/@Field(value = "ficti", type = FieldType.Integer)private Integer ficti;/*** 模板 ID*/@Field(value = "temp_id", type = FieldType.Integer)private Integer tempId;/*** 规格类型*/@Field(value = "spec_type", type = FieldType.Boolean)private Boolean specType;/*** 活动*/@Field(value = "activity", type = FieldType.Text)private String activity;/*** 属性*/@Field(value = "attr", type = FieldType.Text)private String attr;/*** 属性值*/@Field(value = "attr_value", type = FieldType.Text)private String attrValue;/*** 内容*/@Field(value = "content", type = FieldType.Text)private String content;/*** 优惠券 ID 列表*/@Field(value = "coupon_ids", type = FieldType.Text)private String couponIds;/*** 平铺模式*/@Field(value = "flat_pattern", type = FieldType.Text)private String flatPattern;}

✌️ ES常用api

TestES.java

import com.db.test.entity.dto.StoreProductEsDTO;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.*;import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;/*** @author hanson.huang* @version V1.0* @ClassName TestES* @Description es 测试类* @date 2025/2/18 19:41**/
@Slf4j
@SpringBootTest
public class TestES {@Resource(name = "elasticsearchRestTemplate")private ElasticsearchRestTemplate elasticsearchRestTemplate;/*** 生成模拟的 StoreProductEsDTO 对象* @return 模拟对象*/private StoreProductEsDTO generateMockProduct(String id) {StoreProductEsDTO product = new StoreProductEsDTO();product.setId(id);product.setImage("https://example.com/image.jpg");product.setSliderImage("https://example.com/slider_image.jpg");product.setStoreName("测试店铺商品" + id);product.setStoreInfo("这是一个测试用的店铺商品信息");product.setKeyword("测试商品");product.setCateId("1001");product.setUnitName("件");product.setSort(1);product.setIsHot(true);product.setIsBenefit(true);product.setIsBest(true);product.setIsNew(true);product.setIsGood(true);product.setGiveIntegral(10);product.setIsSub(false);product.setFicti(1);product.setTempId(1);product.setSpecType(true);product.setActivity("{\"name\":\"测试活动\"}");product.setAttr("{\"color\":\"red\"}");product.setAttrValue("{\"size\":\"L\"}");product.setContent("商品详细内容描述");product.setCouponIds("{\"id\":\"C001\"}");product.setFlatPattern("{\"mode\":\"平铺\"}");return product;}/*** 插入单条文档*/@Testvoid testInsertDocument() {StoreProductEsDTO product = generateMockProduct("991");StoreProductEsDTO savedProduct = elasticsearchRestTemplate.save(product);log.info("插入文档结果: {}", savedProduct);}/*** 批量插入文档*/@Testvoid testBulkInsertDocuments() {List<StoreProductEsDTO> products = Arrays.asList(generateMockProduct("992"), generateMockProduct("993"));Iterable<StoreProductEsDTO> savedProducts = elasticsearchRestTemplate.save(products);savedProducts.forEach(product -> log.info("批量插入文档结果: {}", product));}/*** 根据 ID 删除文档*/@Testvoid testDeleteDocument() {String id = "997";elasticsearchRestTemplate.delete(id, StoreProductEsDTO.class);log.info("删除 ID 为 {} 的文档", id);}/*** 根据 ID 更新文档*/@Testvoid testUpdateDocument() {StoreProductEsDTO product = generateMockProduct("994");product.setStoreName("更新后的测试店铺商品");StoreProductEsDTO updatedProduct = elasticsearchRestTemplate.save(product);log.info("更新文档结果: {}", updatedProduct);}/*** 查询单条文档*/@Testvoid testSearchSingleDocument() {String id = "992";StoreProductEsDTO product = elasticsearchRestTemplate.get(id, StoreProductEsDTO.class);if (product != null) {log.info("查询到的文档: {}", product);} else {log.info("未查询到 ID 为 {} 的文档", id);}}/*** 查询所有文档*/@Testvoid testSearchAllDocuments() {Query query = new CriteriaQuery(new Criteria());SearchHits<StoreProductEsDTO> searchHits = elasticsearchRestTemplate.search(query, StoreProductEsDTO.class);searchHits.forEach(hit -> log.info("查询到的文档: {}", hit.getContent()));}/*** 分页查询文档*/@Testvoid testSearchDocumentsByPage() {int page = 0;int size = 10;Pageable pageable = PageRequest.of(page, size);Query query = new CriteriaQuery(new Criteria()).setPageable(pageable);SearchHits<StoreProductEsDTO> searchHits = elasticsearchRestTemplate.search(query, StoreProductEsDTO.class);log.info("当前页文档数量: {}", searchHits.getSearchHits().size());// 修正遍历部分List<org.springframework.data.elasticsearch.core.SearchHit<StoreProductEsDTO>> searchHitList = searchHits.getSearchHits();for (org.springframework.data.elasticsearch.core.SearchHit<StoreProductEsDTO> hit : searchHitList) {log.info("分页查询到的文档: {}", hit.getContent());}}/*** 根据条件查询文档*/@Testvoid testSearchDocumentsByCondition() {Criteria criteria = new Criteria("storeName").is("测试店铺商品");Query query = new CriteriaQuery(criteria);SearchHits<StoreProductEsDTO> searchHits = elasticsearchRestTemplate.search(query, StoreProductEsDTO.class);searchHits.forEach(hit -> log.info("根据条件查询到的文档: {}", hit.getContent()));}
}

条件查询中,Criteria是模糊匹配,能查出storeName值为测试店铺商品、‘xxx测试店铺商品’、 ‘测试店铺商品xxx’、 'xxx测试店铺商品xxx’等情况

结果如下:

在这里插入图片描述

☝️ 性能比较

先叠个甲,本次比较非常不专业,数量比较小,MySQL也没设置合适索引,所以本次性能比较不具备参考性

✌️ 模拟创建数据接口

controller

@Resource
private StoreProductService storeProductService;/*** 添加数据** @param storeProductRequest 需要添加的数据* @return*/
@PostMapping("/addData")
public String addData(@RequestBody StoreProductRequest storeProductRequest) {storeProductService.insertData(storeProductRequest);return "success";
}

实现类:StoreProductServiceImpl.java

@Slf4j
@Data
@Service
public class StoreProductServiceImpl extends ServiceImpl<StoreProductMapper, StoreProduct> implements StoreProductService {@Resourceprivate StoreProductMapper storeProductMapper;@Resource(name = "stringRedisTemplate")private StringRedisTemplate stringRedisTemplate;@Resource(name = "elasticsearchRestTemplate")private ElasticsearchRestTemplate elasticsearchRestTemplate;@Resourceprivate StoreProductMongoRepository storeProductMongoRepository;private static final String REDIS_KEY = "storeProduct:key";@Value("${es.storeProduct.indexName:store_product_info_v2}")public String storeProductEsIndexName = "store_product_info_v2";@Value("${es.storeProduct.pageSize:500}")private int storeProductEsListPageSize = 500;@Overridepublic void insertData(StoreProductRequest storeProductRequest) {// 1.插入mysqlStoreProduct storeProduct = new StoreProduct();BeanUtils.copyProperties(storeProductRequest, storeProduct);storeProduct.setActivity(JacksonUtils.jsonEncode(storeProductRequest.getActivity()));storeProduct.setAttr(JacksonUtils.jsonEncode(storeProductRequest.getAttr()));storeProduct.setAttrValue(JacksonUtils.jsonEncode(storeProductRequest.getAttrValue()));storeProduct.setCouponIds(JacksonUtils.jsonEncode(storeProductRequest.getCouponIds()));storeProductMapper.insert(storeProduct);log.warn("数据已经插入mysql数据库:{}", JacksonUtils.jsonEncode(storeProduct));// 2.插入redisstringRedisTemplate.opsForValue().set(REDIS_KEY + storeProduct.getId(), JacksonUtils.jsonEncode(storeProduct));log.warn("数据已经插入redis数据库:{}", JacksonUtils.jsonEncode(storeProduct));// 3.插入mongoMongoStoreProduct mongoStoreProduct = new MongoStoreProduct();BeanUtils.copyProperties(storeProduct, mongoStoreProduct);mongoStoreProduct.setId(storeProduct.getId() + "");try {storeProductMongoRepository.save(mongoStoreProduct);log.warn("数据已经插入mongo数据库:{}", JacksonUtils.jsonEncode(mongoStoreProduct));} catch (Exception e) {log.error("数据插入mongo数据库失败,失败原因:{}", e);}// 4.插入esStoreProductEsDTO storeProductEsDTO = new StoreProductEsDTO();BeanUtils.copyProperties(storeProduct, storeProductEsDTO);storeProductEsDTO.setId(storeProduct.getId() + "");// 创建客户端List<IndexQuery> queries = new ArrayList<>();IndexQuery indexQuery = new IndexQuery();indexQuery.setId(storeProduct.getId() + "");indexQuery.setObject(storeProductEsDTO);queries.add(indexQuery);try {elasticsearchRestTemplate.bulkIndex(queries, StoreProductEsDTO.class);log.warn("数据已经插入es数据库:{}", JacksonUtils.jsonEncode(storeProductEsDTO));} catch (Exception e) {log.error("数据插入es数据库失败,失败原因:{}", e);}}    
}

接口:

curl --location 'localhost:8081/dbTest/addData' \
--header 'Content-Type: application/json' \
--data '{"image": "https://example.com/image.jpg","sliderImage": "https://example.com/slider1.jpg,https://example.com/slider2.jpg","storeName": "新款智能手机","storeInfo": "这是一款高性能智能手机","keyword": "手机,智能手机","cateId": "1,2,3","unitName": "台","sort": 1,"isHot": true,"isBenefit": false,"isBest": true,"isNew": true,"isGood": false,"giveIntegral": 100,"isSub": true,"ficti": 500,"tempId": 1,"specType": true,"activity": ["1", "2", "3"],"attr": [{"attrName": "颜色","attrValues": "红色,蓝色,绿色"},{"attrName": "尺寸","attrValues": "大号,中号,小号"}],"attrValue": [{"productId": 0,"stock": 100,"suk": "红色-大号","price": 1999.00,"image": "https://example.com/red-large.jpg","cost": 1500.00,"otPrice": 2199.00,"weight": 0.5,"volume": 0.1,"brokerage": 100.00,"brokerageTwo": 50.00,"attrValue": "{\"颜色\":\"红色\",\"尺寸\":\"大号\"}","quota": 10,"quotaShow": 10,"minPrice": 1500.00},{"productId": 0,"stock": 150,"suk": "蓝色-中号","price": 1899.00,"image": "https://example.com/blue-medium.jpg","cost": 1400.00,"otPrice": 2099.00,"weight": 0.45,"volume": 0.09,"brokerage": 90.00,"brokerageTwo": 45.00,"attrValue": "{\"颜色\":\"蓝色\",\"尺寸\":\"中号\"}","quota": 15,"quotaShow": 15,"minPrice": 1400.00}],"content": "<p>这是一款高性能智能手机,适合各种场景使用。</p>","couponIds": [1, 2, 3],"flatPattern": "https://example.com/flat-pattern.jpg"
}'

调用这个接口后,分别往四个中间件中插入了id为1000的数据

在这里插入图片描述

MySQL:

在这里插入图片描述
Redis:

在这里插入图片描述

MongoDB:

在这里插入图片描述

ES:

在这里插入图片描述

这样数据算创建完成,现在测试分别查出这条数据需要花费时间

✌️ 查询数据接口

我们使用stopwatch()来统计接口耗时,stopwatch()用法可以参考文章《【StopWatch】使用 StopWatch 统计代码中的耗时操作》

代码如下:

/*** @return 通过四种方式获取数据*/
@Override
public Map<String, Object> getData(Integer id) {Map<String, Object> result = new HashMap<>();// 1.从mysql获取数据StopWatch stopWatch = new StopWatch();stopWatch.start("mysql查询数据开始");StoreProduct storeProduct = storeProductMapper.selectById(id);result.put("mysql", storeProduct);stopWatch.stop();// 2.从redis获取数据stopWatch.start("redis查询数据开始");String redisData = stringRedisTemplate.opsForValue().get(REDIS_KEY + id);result.put("redis", JacksonUtils.jsonDecode(redisData, StoreProduct.class));stopWatch.stop();// 3.从mongo获取数据stopWatch.start("mongo查询数据开始");Optional<MongoStoreProduct> optional = storeProductMongoRepository.findById(String.valueOf(id));if (optional.isPresent()) {MongoStoreProduct mongoStoreProduct = optional.get();result.put("mongo", mongoStoreProduct);}stopWatch.stop();// 4.从es获取数据stopWatch.start("es查询数据开始");StoreProductEsDTO storeProductEsDTO = elasticsearchRestTemplate.get(String.valueOf(id), StoreProductEsDTO.class);result.put("es", storeProductEsDTO);stopWatch.stop();log.error("查询数据耗时:{}", stopWatch.prettyPrint(TimeUnit.MILLISECONDS));return result;
}

调用接口:

在这里插入图片描述
统计耗时:

查询数据耗时:StopWatch '': running time = 87 ms
---------------------------------------------
ms         %     Task name
---------------------------------------------
000000033  38%   mysql查询数据开始
000000012  14%   redis查询数据开始
000000024  28%   mongo查询数据开始
000000017  20%   es查询数据开始

虽然结果不具备参考性,可以看出es和redis性能比较好,在大量数据情况下,就查询数据而言,redis > es > mongoDB > mysql

总结一下:

中间件查询效率性能分析底层存储结构优点缺点使用场景
MySQL中等适用于结构化数据,复杂查询性能较好关系型数据库,使用B+树索引1. 支持复杂查询和事务
2. 数据一致性高
3. 成熟的生态系统和工具支持
1. 大数据量时性能下降
2. 水平扩展较复杂
3. 不适合非结构化数据
1. 金融系统(需要强一致性和事务支持)
2. ERP系统(复杂查询和报表)
3. 传统的关系型数据管理(如用户管理、订单管理)
Redis适用于高并发、低延迟的场景内存键值存储,支持多种数据结构1. 极高的读写性能
2. 支持丰富的数据结构
3. 适合缓存和实时数据处理
1. 数据容量受内存限制
2. 持久化可能影响性能
3. 不适合复杂查询
1. 缓存系统(如网页缓存、会话缓存)
2. 实时排行榜(如游戏积分榜)
3. 消息队列(如任务队列)
4. 实时数据处理(如实时推荐系统)
MongoDB中高适用于半结构化数据,读写性能较好文档型数据库,使用BSON格式存储,支持索引1. 灵活的数据模型
2. 水平扩展容易
3. 适合处理大量非结构化数据
1. 复杂查询性能不如关系型数据库
2. 事务支持较弱(虽然MongoDB 4.0+支持多文档事务)
3. 存储空间占用较大
1. 内容管理系统(CMS)
2. 物联网(IoT)数据存储
3. 日志存储和分析
4. 实时大数据处理(如用户行为分析)
Elasticsearch适用于全文搜索和实时分析分布式搜索引擎,使用倒排索引1. 强大的全文搜索能力
2. 实时数据分析
3. 水平扩展容易
1. 写入性能相对较低
2. 配置和维护复杂
3. 数据一致性较弱(最终一致性)
1. 全文搜索引擎(如电商网站的商品搜索)
2. 日志和指标分析(如ELK Stack)
3. 实时数据分析(如监控和报警系统)
4. 推荐系统(基于用户行为的实时推荐)

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

在这里插入图片描述

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

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

相关文章

51单片机入门_10_数码管动态显示(数字的使用;简单动态显示;指定值的数码管动态显示)

接上篇的数码管静态显示&#xff0c;以下是接上篇介绍到的动态显示的原理。 动态显示的特点是将所有位数码管的段选线并联在一起&#xff0c;由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选&#xff…

C++入门《类和对象》之《运算符重载》详解|成员函数重载/非成员函数重载

C 中&#xff0c;运算符重载是一种特殊的函数&#xff0c;它允许程序员为自定义的数据类型&#xff08;如类和结构体&#xff09;重新定义运算符的行为&#xff0c;使得这些运算符能够像处理内置数据类型一样处理自定义类型的数据。下面将从多个方面详细讲解 C 里的运算符重载。…

Salesforce 检索Layout的设定

做了许多Object&#xff0c;却想不起来怎么设置我的Listview的项目了。 問題&#xff1a; salesforce 最近参照したオブジェクト 表示項目を変更したいですが、「検索レイアウト」の選択メニューが該当オブジェクトのオブジェクトマネージャーから出てないです。 解決方法&am…

SECS/GEM300应用案例参考

GEM300 是一种用于半导体制造领域的通信协议标准&#xff0c;主要用于支持 300mm 晶圆制造的自动化生产。以下是 GEM300 的一些具体应用案例&#xff1a; 1. 半导体设备集成 设备制造商的应用&#xff1a;广州金南瓜科技有限公司通过 GEM300 SDK&#xff0c;帮助国内多个半导体…

win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 win10系统上的虚拟机安装麒麟V10系统提示找不到操作系统&#xff0c;报错&#xff1a;Operating System not found 二、原因分析 国产系统&#xff0c;需要注意的点&#xff1a; 需要看你的系统类…

情书网源码 情书大全帝国cms7.5模板

源码介绍 帝国cms7.5仿《情书网》模板源码&#xff0c;同步生成带手机站带采集。适合改改做文学类的网站。 效果预览 源码获取 情书网源码 情书大全帝国cms7.5模板

C语言题目:链表数据求和操作

题目描述 读入10个复数&#xff0c;建立对应链表&#xff0c;然后求所有复数的和。 输入格式 无 输出格式 无 样例输入 1 2 1 3 4 5 2 3 3 1 2 1 4 2 2 2 3 3 1 1 样例输出 2323i 代码功能概述 createNode 函数&#xff1a; 创建一个包含 10 个复数节点的链表。 每个…

STM32 ADC介绍(硬件原理篇)

目录 背景 AD转换器 采样与保持 量化 编码 AD转换器转换原理 DA转换原理 AD转换原理 1.逐次逼近型AD转换器 2.并联比较型AD转换器 编码器 同步D触发器和边沿D触发器 基本RS触发器 同步RS触发器 同步D触发器 边沿型D触发器&#xff08;维持-阻塞D触发器&#xff…

公网远程家里局域网电脑过程详细记录,包含设置路由器。

由于从校内迁居小区,校内需要远程控制访问小区内个人电脑,于是早些时间刚好自己是电信宽带,可以申请公网ipv4不需要花钱,所以就打电话直接申请即可,申请成功后访问光猫设备管理界面192.168.1.1,输入用户名密码登录超管(密码是网上查下就有了)设置了光猫为桥接模式,然后…

流行编程语言全解析:优势、应用与短板

Python&#xff1a; 优势 Python 以其简洁、易读的语法闻名&#xff0c;新手能快速上手。丰富的库和框架&#xff0c;能极大地提高开发效率。 适用领域 数据科学与分析&#xff1a;处理和分析大规模数据集&#xff0c;进行数据可视化。典型示例&#xff1a;Google 用 Pytho…

统信服务器操作系统V20 1070A 安装docker新版本26.1.4

应用场景&#xff1a; 硬件/整机信息&#xff1a;x86平台、深信服超融合平台 OS版本信息&#xff1a;统信V20 1070a 1.获取docker二进制包 链接: https://pan.baidu.com/s/1SukBlra0mQxvslTfFakzGw?pwd5s5y 提取码: 5s5y tar xvf docker-26.1.4.tgz groupadd docker ch…

在 Vue 3 中使用 Lottie 动画:实现一个加载动画

在现代前端开发中&#xff0c;动画是提升用户体验的重要元素之一。Lottie 是一个流行的动画库&#xff0c;它允许我们使用 JSON 文件来渲染高质量的动画。本文将介绍如何在 Vue 3 项目中集成 Lottie 动画&#xff0c;并实现一个加载动画效果。 如果对你有帮助请帮忙点个&#x…

【Spring】Spring配置文件

目录 ​什么是配置文件&#xff1f; 配置文件的作用 SpringBoot配置文件 配置文件格式 配置文件的优先级 properties配置文件说明 properties基本语法 读取配置文件 properties缺点 yml配置文件说明 yml基本语法 使用yml连接数据库 yml配置不同数据类型及null 注意…

蓝桥杯篇---实时时钟 DS1302

文章目录 前言特点简介1.低功耗2.时钟/日历功能3.32字节的额外RAM4.串行接口 DS1302 引脚说明1.VCC12.VCC23.GND4.CE5.I/O6.SCLK DS1302 寄存器1.秒寄存器2.分钟寄存器3.小时寄存器4.日寄存器5.月寄存器6.星期寄存器7.年寄存器8.控制寄存器 DS1302 与 IAP25F2K61S2 的连接1.CE连…

Dubbo:高效的分布式服务框架

引言 在当今互联网应用的快速发展中&#xff0c;微服务架构已经成为一种主流的设计模式&#xff0c;它将一个大型单体应用拆分成多个小型、松耦合的服务。Dubbo 作为一款由阿里巴巴开源的 RPC 服务框架&#xff0c;专门为解决分布式系统中服务通信和治理的问题而设计。本文将深…

Visual Studio Code使用ai大模型编成

1、在Visual Studio Code搜索安装roo code 2、去https://openrouter.ai/settings/keys官网申请个免费的配置使用

【Javascript Day18】

目录 标签事件绑定的属性参数 阻止默认行为 dialog的实现及组织冒泡&#xff08;捕获&#xff09;传递 基于冒泡的事件委托 键盘事件的事件源对象信息 JS的自动触发操作 标签事件绑定的属性参数 <!-- 标签上的事件绑定&#xff0c;事件源对象通过 关键字event传递 --…

解锁机器学习核心算法 | 支持向量机:机器学习中的分类利刃

一、引言 在机器学习的庞大算法体系中&#xff0c;有十种算法被广泛认为是最具代表性和实用性的&#xff0c;它们犹如机器学习领域的 “十大神器”&#xff0c;各自发挥着独特的作用。这十大算法包括线性回归、逻辑回归、决策树、随机森林、K - 近邻算法、K - 平均算法、支持向…

玩客云 IP查找

1.玩客云使用静态IP在不同网段路由器下不能使用&#xff0c;动态不好找IP地址 1.1使用python3 实现自动获取发送 import requests import os import socket# 从环境变量获取 PushPlus 的 token 和群组编码 PUSH_PLUS_TOKEN os.getenv("PUSH_PLUS_TOKEN") PUSH_PLU…

Linux(Centos 7.6)命令详解:cat

1.命令作用 将文件或标准输入连接到标准输出(Concatenate FILE(s), or standard input, to standard output)&#xff0c; 即将文件内容输出到屏幕上&#xff0c;或者将多个文件合并成一个文件。 2.命令语法 Usage: cat [OPTION]... [FILE]... 3.参数详解 OPTION: -A, -…