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

embedded/2025/2/21 7:58:59/

常见中间件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 espace">com.db.test.entity.dto.StoreProductEsDTO;
import espace">lombok.extern.slf4j.Slf4j;
import espace">org.junit.jupiter.api.Test;
import espace">org.springframework.boot.test.context.SpringBootTest;
import espace">org.springframework.data.domain.PageRequest;
import espace">org.springframework.data.domain.Pageable;
import espace">org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import espace">org.springframework.data.elasticsearch.core.SearchHits;
import espace">org.springframework.data.elasticsearch.core.query.*;import espace">javax.annotation.Resource;
import espace">java.util.Arrays;
import espace">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<espace">org.springframework.data.elasticsearch.core.SearchHit<StoreProductEsDTO>> searchHitList = searchHits.getSearchHits();for (espace">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查询数据开始

虽然结果不具备参考性,可以看出esredis性能比较好,在大量数据情况下,就查询数据而言,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.ppmy.cn/embedded/163517.html

相关文章

Dockerfile 编写推荐

一、导读 本文主要介绍在编写 docker 镜像的时候一些需要注意的事项和推荐的做法。 虽然 Dockerfile 简化了镜像构建的过程&#xff0c;并且把这个过程可以进行版本控制&#xff0c;但是不正当的 Dockerfile 使用也会导致很多问题。 docker 镜像太大。如果你经常使用镜像或者…

通俗诠释 DeepSeek-V3 模型的 “671B” ,“37B”与 “128K”,用生活比喻帮你理解模型的秘密!

欢迎来到涛涛聊AI。 在DeepSeek-V3模型的参数描述中&#xff0c;你可能会看到类似“671B 37B 128K”这样的标记。这些字母和数字的组合看起来像密码&#xff0c;但其实它们揭示了模型的“大脑容量”和“工作方式”。我们用日常生活的比喻来解释&#xff1a; 一、数字含义&…

golang常用库之-swaggo/swag根据注释生成接口文档

golang常用库之-swaggo/swag库根据注释生成接口文档 什么是swaggo/swag github&#xff1a;https://github.com/swaggo/swag 参考文档&#xff1a;https://golang.halfiisland.com/community/pkgs/web/swag.html#%E4%BD%BF%E7%94%A8 swaggo/swag 是 Swagger API 2.0 在 go 语…

Docker 在微服务架构中的应用(一)

一、引言 在当今数字化快速发展的时代&#xff0c;软件应用的规模和复杂度与日俱增。传统的单体架构在应对大规模、高并发以及快速迭代的业务需求时&#xff0c;逐渐显得力不从心。于是&#xff0c;微服务架构应运而生&#xff0c;它将一个大型的应用程序拆分成多个小型、独立…

利用acme.sh 申请 Google 免费证书

1.Google API权限准备 获取 EAB 密钥 ID 和 HMAC 登录你的 GCP 控制台面板&#xff0c;进入 Public Certificate Authority API 管理页面&#xff08;https://console.cloud.google.com/apis/library/publicca.googleapis.com&#xff09;点击启动&#xff1a; 或者直接在下一…

java:用Guava的TypeToken优雅处理通配符类型(WildcardType): ? extends Number

在日常开发中我们经常会遇到泛型和通配符类型&#xff08;WildcardType&#xff09;&#xff0c;比如当我们需要处理List<? extends Number>这样的类型时&#xff0c;如何优雅地创建这样的类型表示&#xff1f;本文将重点介绍如何通过Guava的TypeToken来实现通配符类型的…

自有证书的rancher集群使用rke部署k8s集群异常

rancher使用自签域名或者商业证书容易踩到的坑。 最开始的报错&#xff1a; docker logs kubelet‘s id E0214 13:04:14.590268 9614 pod_workers.go:1300] "Error syncing pod, skipping" err"failed to \"StartContainer\" for \"clust…

实现LED流水灯的几种方法

1.实现原理 通过不断给P1中不同的IO口置低电平&#xff0c;从而达到LED流水灯的效果。 2.程序思路 方法一&#xff1a;通过给P1口赋不同的值从而达到流水灯的效果 /* 头文件声明区域 */ #include <REGX52.H>/* 延时函数 */ //需要记&#xff01;&#xff01;&#…