使用Redis缓解数据库压力+三种常见问题

目录

一.如何使用 Redis 缓存减缓数据库的压力 :

二.Redis缓存穿透:

布隆过滤器的工作原理:

三.缓存雪崩:

缓存雪崩的处理策略:

四.缓存击穿:


使用 Redis 缓存来减缓数据库的压力是一个常见的优化手段,尤其在高并发、数据访问量大的应用场景中。通过将经常访问的数据存储在 Redis 中,可以大大减少对数据库的访问频率,提升系统的响应速度。

在我们web项目中是有多种地方可以使用缓存的:

下面带来如何使用Redis减轻数据库压力:


一.如何使用 Redis 缓存减缓数据库的压力 :

通常情况下,我们对于查询数据库的场景都是直接使用框架进行查询数据库,但是这样在浏览量很多的情况下就会造成数据库压力,所以这个时候我们可以使用Redis进行缓解数据库的压力。

方法如下: 

  • 当应用需要查询数据库时,首先检查 Redis 缓存中是否存在该数据。
  • 如果数据存在,则直接从 Redis 返回数据(缓存命中),无需访问数据库。
  • 如果数据不存在,则从数据库中查询,并将查询结果缓存到 Redis 中,以便下次访问。

我们看下面图片:

下面我们基于上述图片步骤来编写代码:

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if(StrUtil.isNotBlank(shopJson)){// 不为空,反序列化传回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}// Redis内不存在,则查询数据库Shop shop = getById(id);if(shop == null){return Result.fail("店铺不存在");}// 存在就写入缓存stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);return Result.ok(shop);}
}

我们判断Redis缓存内是否有待查询信息,如果有(缓存命中)就直接在缓存中拿取,如果没有则在数据库中取出,并写入缓存以便于下一次的查询直接在Redis获取来做到减轻数据库压力。


 上面的方法是读取操作,那么如何进行写入数据库操作呢?

当我们在更新数据库中的数据时,应该同时更新或删除 Redis 中的缓存,以确保缓存中的数据是最新的并且数据一致性而不出现数据不同步的情况。

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;// 缓存更新策略@Override@Transactional(rollbackFor = Exception.class)public Result update(Shop shop) {// 先更新数据库在删除缓存if(shop.getId() == null){return Result.fail("店铺id不能为空");}updateById(shop); // 更新数据库stringRedisTemplate.delete("cache:shop:" + shop.getId()); // 删除缓存return Result.ok();}
}

@Transactional(rollbackFor = Exception.class):这表示该方法是一个事务性方法。即在方法执行过程中,如果发生任何异常(Exception 或其子类),事务将会回滚,确保数据库更新操作的原子性和一致性。这里 rollbackFor = Exception.class 表示对于所有类型的异常都回滚。

先更新数据库,再删除缓存的好处是什么?为什么要这样处理?不能先删缓存后更新数据库吗?

更新数据库后再删除缓存 能确保 数据库操作先行,这意味着无论在删除缓存的过程中发生什么问题,至少数据库中的数据已经更新。如果缓存先被删除再进行数据库更新,可能会导致数据更新失败但缓存已经被删除,这样下一次访问就会直接从数据库读取数据,结果可能是错误的或者导致不一致的状态。比如,数据库更新操作由于异常回滚了,但缓存已经被删除,接下来的请求可能会读取到不一致的空数据。

先删除缓存再更新数据库,意味着 删除缓存后可能会存在一段空缓存的时间。如果这个时间较长,系统会频繁查询数据库,增加了数据库的负载。理论上,如果数据库操作失败,缓存会空,导致很多请求直接去查询数据库,造成大量数据库压力。


所以我们要保证数据库的数据始终是最新的,无论缓存删除过程中出现什么问题,数据库中的数据始终是最新的而不会造成数据不一致情况发生;同时,删除缓存后,下次请求会重新加载更新后的数据,避免缓存和数据库数据不一致。如果先删除缓存,可能导致数据不一致、系统复杂度增加,甚至可能导致缓存穿透等问题。


在上面的过程我们可以非常直观的看出缓存的好处,但是正如有好也有坏,Redis也不例外。

二.Redis缓存穿透:

问题分析:缓存穿透指的是查询一个不存在的数据,缓存中没有而数据库也没有,导致每次请求都会访问数据库,造成数据库压力增大。 


解决方案:对于不存在的数据,可以在 Redis 中存储一个空值(如空对象或特殊标记),并为这些空值设置短时间的过期时间,这样后续相同的请求就可以直接返回缓存中的空值,避免多次查询数据库。

首先介绍缓存空对象方法:

 我们先根据id查询缓存内的数据,如果不为空则直接反序列化返回数据,但是如果命中数据是个空值,我们就需要返回错误信息,如果Redis不存在,那么在查询数据化的结果没有值的情况需要将空值写入Redis并设置过期时间。

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;public Shop queryWithPassThrough(Long id){String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if(StrUtil.isNotBlank(shopJson)){// 不为空,反序列化传回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}// 判断命中的是否为空值if(Objects.equals(shopJson, "")){// 返回错误信息return null;}// Redis内不存在,则查询数据库Shop shop = getById(id);if(shop == null){ // 判断数据库内是否有值// 将空值写入RedisstringRedisTemplate.opsForValue().set("cache:shop:" + id, "", 30L, TimeUnit.MINUTES);return null;}// 存在就写入缓存stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);return shop;}// 缓存穿透的解决办法:缓存空对象 / 布隆过滤@Overridepublic Result queryById(Long id) {// 缓存穿透Shop shop = queryWithPassThrough(id);return Result.ok(shop);}

那么如何使用布隆过滤来解决缓存穿透问题呢?

在上面代码中,我们已经采取了缓存空对象的方式来避免缓存穿透问题。具体来说,当数据库查询不到对应的数据时,会将空对象(即 "")存入 Redis,这样可以防止同一个无效请求在短时间内反复查询数据库

为了进一步提升缓存穿透的防范能力,我们可以结合 布隆过滤器(Bloom Filter) 来增强这一机制。布隆过滤器是一种空间效率高的概率数据结构,用于测试某个元素是否在一个集合中。它可能会有少量的误报(即判断一个元素在集合中时会误报为存在),但绝不会漏掉任何元素。


使用布隆过滤器解决缓存穿透:
 

布隆过滤器可以用来预先判断一个元素是否存在,在查询数据库前,通过布隆过滤器进行初步筛查,避免对数据库的无效查询。例如,对于每一个可能存在的店铺 ID,我们可以将其先加入布隆过滤器。然后在每次查询时,首先通过布隆过滤器判断该店铺是否存在,如果布隆过滤器判断不存在,那么就直接返回 null,避免访问数据库。

在此之前,我们首先需要引入一个布隆过滤器的库,常见的布隆过滤器库有 guavaredisson 等:

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>

我们首先创建一个 BloomFilterConfig 配置类来初始化一个布隆过滤器。该过滤器会存储所有已查询过的店铺 ID(即存在于数据库中的 ID)。

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class BloomFilterConfig {// 定义布隆过滤器的参数private static final int EXPECTED_INSERTIONS = 1000000; // 预期插入元素数量private static final double FPP = 0.01; // 允许的误判率(1%)@Beanpublic BloomFilter<Long> shopBloomFilter() {return BloomFilter.create(Funnels.longFunnel(), EXPECTED_INSERTIONS, FPP);}
}

在上面的 ShopServiceImpl 类中,我们通过 @Resource 注解将布隆过滤器注入,并且在查询方法中使用了它。此时我们就已经可以通过布隆过滤器来判断店铺 ID 是否存在。 

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;// 注入布隆过滤器@Resourceprivate BloomFilter<Long> shopBloomFilter;public Shop queryWithPassThrough(Long id) {String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if (StrUtil.isNotBlank(shopJson)) {// 如果缓存命中,反序列化返回数据Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}// 判断命中的是否为空值if (Objects.equals(shopJson, "")) {// 返回错误信息return null;}// 先通过布隆过滤器检查该店铺是否存在if (!shopBloomFilter.mightContain(id)) {// 如果布隆过滤器判定该店铺ID不存在,则直接返回 null,避免查询数据库return null;}// Redis内不存在,则查询数据库Shop shop = getById(id);if (shop == null) { // 判断数据库内是否有值// 将空值写入Redis,并加入布隆过滤器,避免下次访问stringRedisTemplate.opsForValue().set("cache:shop:" + id, "", 30L, TimeUnit.MINUTES);// 将店铺ID添加到布隆过滤器shopBloomFilter.put(id);return null;}// 数据存在,将结果写入缓存stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop), 30L, TimeUnit.MINUTES);return shop;}// 缓存穿透的解决办法:缓存空对象 / 布隆过滤@Overridepublic Result queryById(Long id) {// 使用布隆过滤器进行查询Shop shop = queryWithPassThrough(id);return Result.ok(shop);}
}

布隆过滤器的工作原理:

  • BloomFilter.create():用于创建布隆过滤器。你可以通过 Funnels.longFunnel()Long 类型的 ID 插入到布隆过滤器中,EXPECTED_INSERTIONS 是你预计会插入的元素数量,FPP(False Positive Probability)是你允许的误判率,越低的误判率意味着需要更大的内存。

  • shopBloomFilter.mightContain(id):检查布隆过滤器中是否包含店铺 ID。如果包含,继续执行数据库查询操作;如果不包含,直接返回 null

  • shopBloomFilter.put(id):将店铺 ID 插入到布隆过滤器中,表示该店铺 ID 已经查询过。

三.缓存雪崩:

问题分析:缓存中大量数据在同一时刻过期或者缓存服务器挂掉导致无法访问缓存。当大量请求同时到达时,由于缓存失效,它们会直接查询数据库,导致数据库的压力瞬间增大,可能会导致数据库宕机或出现性能瓶颈。


解决方案:①缓存数据的过期时间加随机值  ②利用Redis集群提高服务的可用性  ③给缓存业务添加降级限流策略  ④给业务添加多级缓存高并发处理 --- Caffeine内存缓存库

为了处理 缓存雪崩,我们需要确保缓存失效时,不会造成大量的数据库查询压力。我们可以通过给每个缓存设置 不同的过期时间,同时增加 本地缓存预热缓存 来防止缓存雪崩。

在上面代码中,我们已经使用了布隆过滤器和缓存空对象策略来防止 缓存穿透。现在我们需要修改代码来添加 缓存雪崩的处理,特别是为每个缓存设置 随机过期时间,并且通过引入本地缓存来减少对 Redis 的依赖。

缓存雪崩的处理策略:

  • 为每个缓存设置随机过期时间:避免大量缓存同时过期,导致雪崩现象。
  • 使用本地缓存作为二级缓存:减少数据库的负担,特别是 Redis 服务不可用时。
  • 预热缓存:在系统启动时,预先加载常用数据到缓存中,避免缓存空缺。

下面我们指展示加入本地缓存(Cache)来缓解缓存压力: 

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;// 注入布隆过滤器@Resourceprivate BloomFilter<Long> shopBloomFilter;// 本地缓存(Guava Cache)private Cache<Long, Shop> localCache = CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // 本地缓存的过期时间.build();// 随机生成缓存的过期时间,防止雪崩private long getRandomExpireTime() {return 10 + (long) (Math.random() * 20);}public Shop queryWithPassThrough(Long id) {// 先查询本地缓存Shop shop = localCache.getIfPresent(id);if (shop != null) {return shop; // 本地缓存命中}// 查询 Redis 缓存String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if (StrUtil.isNotBlank(shopJson)) {// 缓存命中,反序列化并返回shop = JSONUtil.toBean(shopJson, Shop.class);// 将 Redis 缓存的结果加入本地缓存localCache.put(id, shop);return shop;}// 如果缓存中没有数据,判断是否是空对象if (Objects.equals(shopJson, "")) {return null; // 返回空值}// 使用布隆过滤器判断该店铺ID是否存在if (!shopBloomFilter.mightContain(id)) {return null; // 如果布隆过滤器判定该店铺不存在,则不查询数据库}// 从数据库查询shop = getById(id);if (shop == null) {// 如果数据库也没有数据,写入空值到 Redis,并加入布隆过滤器stringRedisTemplate.opsForValue().set("cache:shop:" + id, "", 30L, TimeUnit.MINUTES);shopBloomFilter.put(id); // 加入布隆过滤器,防止缓存穿透return null;}// 如果数据库存在数据,更新缓存并返回stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop), getRandomExpireTime(), TimeUnit.MINUTES);// 更新本地缓存localCache.put(id, shop);return shop;}// 更新店铺方法(确保本地缓存与外部缓存一致)@Override@Transactional(rollbackFor = Exception.class)public Result update(Shop shop) {// 更新数据库if(shop.getId() == null) {return Result.fail("店铺id不能为空");}updateById(shop); // 更新数据库// 删除本地缓存和Redis缓存localCache.invalidate(shop.getId());  // 删除本地缓存stringRedisTemplate.delete("cache:shop:" + shop.getId());  // 删除Redis缓存// 将新的数据写入缓存stringRedisTemplate.opsForValue().set("cache:shop:" + shop.getId(), JSONUtil.toJsonStr(shop), getRandomExpireTime(), TimeUnit.MINUTES);localCache.put(shop.getId(), shop); // 更新本地缓存return Result.ok();}@Overridepublic Result queryById(Long id) {// 使用缓存穿透的策略查询店铺Shop shop = queryWithPassThrough(id);return Result.ok(shop);}
}

四.缓存击穿:

问题分析:缓存击穿是指当缓存中的一个或多个被高并发访问并且缓存重建业务较为复杂的key突然失效(或未命中)时,多个请求同时访问该数据导致直接查询数据库,数据库瞬间压力剧增,系统性能急剧下降。


场景分析:当某个缓存中的数据被清空或过期(例如缓存的数据为常用数据),多个并发请求同时访问时,都会直接访问数据库,造成数据库负载过高。

解决方案:

  • 互斥锁:通过分布式锁(如 Redis 的 SETNX 命令)来限制在同一时刻只有一个线程能查询数据库并更新缓存,避免并发请求造成数据库压力。
  • 提前加载缓存:在缓存失效时,可以提前加载缓存,避免每次都查询数据库。
  • 缓存预热:在系统启动时,加载常用的数据到缓存中,以减少数据库访问。

1.使用互斥锁: 

在上面代码中,如果缓存中的数据失效了,多个请求可能会同时查询数据库并更新缓存,导致数据库压力过大。为了解决这个问题,我们可以引入互斥锁,确保同一时刻只有一个请求会查询数据库并更新缓存。

使用 Redis 锁来防止缓存击穿。当某个缓存失效时,只有一个请求会查询数据库,其他请求会等待,直到该请求完成数据库查询和缓存更新。

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {// 互斥锁解决缓存击穿Shop shop = queryWithMutex(id);if(shop == null){return Result.fail("店铺不存在");}return Result.ok(shop);}public Shop queryWithMutex(Long id){String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if(StrUtil.isNotBlank(shopJson)){// 不为空,反序列化传回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}// 判断命中的是否为空值if(Objects.equals(shopJson, "")){// 返回错误信息return null;}// 实现缓存重建// 获取互斥锁String lockKey = "lock:shop:" + id;Shop shop = null;try {boolean isLock = tryLock(lockKey);// 判断锁是否获取成功if(!isLock){// 失败则休眠重试Thread.sleep(50);return queryWithMutex(id);}// 成功获取锁后根据id查询数据库shop = getById(id);// 模拟重建的演示Thread.sleep(200);if(shop == null){ // 判断数据库内是否有值// 将空值写入RedisstringRedisTemplate.opsForValue().set("cache:shop:" + id, "", 30L, TimeUnit.MINUTES);return null;}// 存在就写入缓存stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 释放互斥锁unLock(lockKey);}return shop;}private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "", 10L, TimeUnit.MINUTES);return BooleanUtil.isTrue(flag);}private void unLock(String key){stringRedisTemplate.delete(key);}}

2.使用逻辑过期:

逻辑过期的核心思路是:缓存的过期时间不是物理时间的直接过期,而是通过设置一个“逻辑过期时间”来控制缓存的数据是否过期。在本段代码中,缓存的数据有一个逻辑过期时间expireTime,如果当前时间已经超过了该时间,认为缓存已经过期,但数据本身并没有立即失效。

逻辑过期的核心思想:缓存数据即使已经过期但是仍然存在,并不会立即被删除,而是依赖一个 "逻辑过期时间" 来判断是否重新加载缓存。在我们下面代码中,expireTime 表示缓存数据的逻辑过期时间。

 在这里我们先创建一个RedisData类去封装存入Redis的信息:

  1. expireTime:封装过期时间(以便于判断是否过期)
  2. data:存入缓存的数据
import lombok.Data;
import java.time.LocalDateTime;@Data
public class RedisData {private LocalDateTime expireTime; // 过期时间private Object data;
}

通过 逻辑过期分布式锁,下面代码解决了这个问题:

  1. 逻辑过期:缓存数据仍然存在,即使它已经过期,其他请求仍然可以获取到已过期的数据,这样就避免了大量请求都打到数据库。即使缓存数据“过期”,它仍然可以短时间内使用。
  2. 分布式锁:通过分布式锁,确保只有一个请求会去数据库查询数据并更新缓存。其他请求会在缓存重建期间依然使用过期数据,不会同时访问数据库。这样就避免了多次请求同时去数据库查询,造成数据库的压力过大。

热点 key 的问题通常是因为缓存的失效导致大量并发请求打到数据库,但通过逻辑过期和分布式锁的配合使用,可以避免这个问题:

  • 只有一个请求会去重建缓存,其他请求在缓存重建期间依然能够使用过期的缓存。
  • 在重建缓存期间,避免了大量并发请求同时访问数据库。
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {// 创建一个固定大小的线程池,用于异步重建缓存,最大线程数为10private static final ExecutorService CACHE_REEBUILD_EXECUTOR = Executors.newFixedThreadPool(10);@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {// 使用逻辑过期的方式查询店铺,避免缓存击穿Shop shop = queryWithLogicalExpire(id);// 如果店铺信息为null,表示未找到或店铺不存在if(shop == null){return Result.fail("店铺不存在");}return Result.ok(shop); // 返回店铺信息}// 尝试获取分布式锁,防止并发重建缓存private boolean tryLock(String key){// 设置Redis中的lockKey并设置锁的过期时间为10分钟Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "", 10L, TimeUnit.MINUTES);// 如果flag为true,表示获取锁成功,返回truereturn BooleanUtil.isTrue(flag);}// 释放分布式锁private void unLock(String key){// 删除Redis中的锁,释放资源stringRedisTemplate.delete(key);}// 将店铺数据保存到Redis中,设置逻辑过期时间private void saveShop2Redis(Long id, Long expireSeconds){// 查询店铺数据Shop shop = getById(id);// 封装逻辑过期时间RedisData redisData = new RedisData();redisData.setData(shop); // 存储店铺数据redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireSeconds)); // 设置过期时间// 将封装好的数据保存到Redis中stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(redisData));}// 使用逻辑过期解决缓存击穿问题public Shop queryWithLogicalExpire(Long id){// 从Redis缓存中查询店铺数据String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);// 判断缓存中是否存在数据if(StrUtil.isBlank(shopJson)){// 如果缓存为空,则代表没有缓存数据,直接返回nullreturn null;}// 将从Redis获取到的JSON字符串转换为RedisData对象RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);// 获取存储的数据部分(店铺信息)JSONObject data = (JSONObject) redisData.getData();// 将数据部分转换为Shop对象Shop shop = JSONUtil.toBean(data, Shop.class);// 获取缓存的逻辑过期时间LocalDateTime expireTime = redisData.getExpireTime();// 判断逻辑过期时间是否已过if(expireTime.isAfter(LocalDateTime.now())){// 如果未过期,则直接返回缓存的店铺信息return shop;}// 如果缓存已过期,尝试获取分布式锁来防止多个请求同时去查询数据库并重建缓存String lockKey = "lock:shop:" + id;boolean flag = tryLock(lockKey); // 尝试获取锁if(flag){// 如果成功获取锁,启动独立线程来重建缓存CACHE_REEBUILD_EXECUTOR.submit(() -> {try {// 异步执行缓存重建this.saveShop2Redis(id, 30L); // 重新保存缓存,设置过期时间为30秒} catch (Exception e) {// 异常处理throw new RuntimeException(e);} finally {// 重建缓存完成后,释放锁unLock(lockKey);}});}// 返回店铺信息,不管缓存是否过期,都可以直接返回缓存的店铺数据(避免缓存击穿)return shop;}
}

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

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

相关文章

移动端VR处理器和传统显卡的不同

骁龙 XR 系列芯片 更多地依赖 AI 技术 来优化渲染过程&#xff0c;而传统的 GPU 渲染 则倾向于在低画质下运行以减少负载。这种设计是为了在有限的硬件资源下&#xff08;如移动端 XR 设备&#xff09;实现高性能和低功耗的平衡。以下是具体的分析&#xff1a; 1. AI 驱动的渲染…

AI新玩法:Flux.1图像生成结合内网穿透远程生图的解决方案

文章目录 前言1. 本地部署ComfyUI2. 下载 Flux.1 模型3. 下载CLIP模型4. 下载 VAE 模型5. 演示文生图6. 公网使用 Flux.1 大模型6.1 创建远程连接公网地址 7. 固定远程访问公网地址 前言 在这个AI技术日新月异的时代&#xff0c;图像生成模型已经成为了创意工作者和开发者手中…

WordPress果果对象存储插件

将网站上的图片等静态资源文件上传至七牛云对象存储&#xff0c;可以减轻服务器文件存储压力&#xff0c;提升静态文件访问速度&#xff0c;从而加速网站访问速度。 支持&#xff1a;阿里云对象存储、华为云对象存储、百度云对象存储、腾讯云对象存储、七牛云对象存储。 下载…

2025美赛倒计时,数学建模五类模型40+常用算法及算法手册汇总

数学建模美赛倒计时&#xff0c;对于第一次参加竞赛且没有相关基础知识的同学来讲&#xff0c;掌握数学建模常用经典的模型算法知识&#xff0c;并熟练使用相关软件进行建模是关键。本文将介绍一些常用的模型算法&#xff0c;以及软件操作教程。 数学建模常用模型包括&#xf…

Maven的下载安装配置

maven的下载安装配置 maven是什么 Maven 是一个用于 Java 平台的 自动化构建工具&#xff0c;由 Apache 组织提供。它不仅可以用作包管理&#xff0c;还支持项目的开发、打包、测试及部署等一系列行为 Maven的核心功能 项目构建生命周期管理&#xff1a;Maven定义了项目构建…

2000-2010年各省第三产业就业人数数据

2000-2010年各省第三产业就业人数数据 1、时间&#xff1a;2000-2010年 2、来源&#xff1a;统计年鉴、各省年鉴 3、指标&#xff1a;行政区划代码、地区、年份、第三产业就业人员数&#xff08;万人&#xff09; 4、范围&#xff1a;31省 5、指标解释&#xff1a;第三产业…

【系统环境丢失恢复】如何恢复和重建 Ubuntu 中的 .bashrc 文件

r如果你遇到这种情况&#xff0c;说明系统环境的.bashrc 文件丢失恢复&#xff1a; 要恢复 ~/.bashrc 文件&#xff0c;可以按照以下几种方式操作&#xff1a; 恢复默认的 ~/.bashrc 文件 如果 ~/.bashrc 文件被删除或修改&#xff0c;你可以恢复到默认的版本。可以参考以下…

Docker网段和服务器ip冲突导致无法访问网络的解决方法

若宿主机所在网络的网段为172.[17-31].xx.xx&#xff0c;则会与Docker本身内部网络间出现冲突&#xff0c;此时需要重新配置Docker默认地址池 一&#xff1a;查看docker的默认网段 route 二&#xff1a;修改docker的默认网段 etc/docker/daemon.json文件增加修改网段信息 {…

2.2.1 语句结构

ST&#xff08;Structured Text&#xff09;语言是一种基于IEC 61131-3标准的高级文本编程语言&#xff0c;其语法规则严格且清晰。以下是ST语言中关于分号、注释和代码块的详细语法规则说明&#xff1a; 分号&#xff08;;&#xff09; 作用&#xff1a;分号用于表示语句的结…

音频入门(二):音频数据增强

本文介绍了一些常见的音频数据增强方法&#xff0c;并给出了代码实现。 目录 一、简介 二、代码 1. 安装必要的库 2. 代码 3. 各函数的介绍 4. 使用方法 参考&#xff1a; 一、简介 音频数据增强是机器学习和深度学习领域中用于改善模型性能和泛化能力的技术。 使用数据…

网络(一)

目录 1. 网络基础(一); 2. 网络套接字; 3. TCP实现; 1. 网络基础(一) 1.1 网络发展: 从一个个计算器都是独立的, 到计算机连接起来进行数据共享, 后期计算机数量很多通过交换器和路由器进行传输(局域网). 广域网就是世界各个计算器进行数据共享, 也是由一个个局域网组成. 1…

风光并网对电网电能质量影响的matlab/simulink仿真建模

这个课题早在一几年的时候比较热门&#xff0c;之前作电科院配电网的一个项目中也有所涉及&#xff0c;我把其中一部分经典仿真模型思路分享给大家&#xff0c;电能质量影响这部分&#xff0c;我在模型中主要体现的就是不同容量的光伏、风电接入&#xff0c;对并网点的电压影响…

【深度学习入门】深度学习知识点总结

一、卷积 &#xff08;1&#xff09;什么是卷积 定义&#xff1a;特征图的局部与卷积核做内积的操作。 作用&#xff1a;① 广泛应用于图像处理领域。卷积操作可以提取图片中的特征&#xff0c;低层的卷积层提取局部特征&#xff0c;如&#xff1a;边缘、线条、角。 ② 高层…

MySQL(4)多表查询

引言&#xff1a;为什么需要多表的查询&#xff1f; A&#xff1a;提高效率&#xff0c;多线进行。 高内聚、低耦合。 一、多表查询的条件 1、错误的多表查询&#xff1a; SELECT employee_id,department_name FROM employees,departments; SELECT employee_id,department…

PIC单片机HEX文件格式分析

在调试PIC单片机在bootloader程序时&#xff0c;需要将hex文件转换为bin文件&#xff0c;在转换之前先了解一下hex文件中数据是如何定义的。 直接打开一个LED灯闪烁的程序生成的hex文件&#xff0c;芯片型号为PIC18F46K80 可以看到每条数据都是由6部分组成的&#xff0c;下面分…

CANoe Trace窗口

文章目录 一、Trace窗口简介二、Trace窗口打开三、Trace窗口菜单栏介绍1. Detail View2. Statistic View3. Difference view4. Predefined filter5. Analysis filter6. Toggle time mode7. Toggle display mode8. Change font size 四、Trabe窗口配置1. 打开 Trace配置窗口2. 增…

c#配置config文件

1&#xff0c;引用命名空间 Configuration 及配置信息

idea新增java快捷键代码片段

最近在写一些算法题&#xff0c;有很多的List<List这种编写&#xff0c;想着能否自定义一下快捷键 直接在写代码输入&#xff1a;lli&#xff0c;即可看见提示

vim练级攻略(精简版)

vim推荐配置: curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh 0. 规定 Ctrl-λ 等价于 <C-λ> :command 等价于 :command <回车> n 等价于 数字 blank字符 等价于 空格&#xff0c;tab&am…

鸿蒙参考文档和问题记录

本文用于记录鸿蒙使用过程中的问题和相关参考文档 问题记录 1. 兼容性测试套件问题 ActsStartAbilityForResultNoTargetBundleListStageTest套件测试失败&#xff1a;模块FreeInstall 技术资料 1. HarmonyOS应用如何打包HAP并安装到真机 HarmonyOS应用如何打包HAP并安装到真…