|
@@ -715,50 +715,57 @@ ln: 自然对数是以常数e为[底数](https://baike.baidu.com/item/底数/541
|
|
|
|
|
|
## 3.2 初始化布隆过滤器
|
|
|
|
|
|
-在`service-search` 模块的启动类中添加
|
|
|
+在`service-album` 模块的启动类中添加
|
|
|
|
|
|
```java
|
|
|
package com.atguigu.tingshu;
|
|
|
|
|
|
import com.atguigu.tingshu.common.constant.RedisConstant;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.redisson.api.RBloomFilter;
|
|
|
import org.redisson.api.RedissonClient;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.boot.CommandLineRunner;
|
|
|
import org.springframework.boot.SpringApplication;
|
|
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
|
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
|
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
|
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
|
|
-import org.springframework.scheduling.annotation.EnableAsync;
|
|
|
+import org.springframework.scheduling.annotation.EnableScheduling;
|
|
|
|
|
|
-@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
|
|
|
+@Slf4j
|
|
|
+@SpringBootApplication
|
|
|
@EnableDiscoveryClient
|
|
|
-@EnableFeignClients//(basePackages = {"a","b"}) //确保能够扫描到依赖中OpenFeign接口
|
|
|
-@EnableAsync
|
|
|
-public class ServiceSearchApplication implements CommandLineRunner {
|
|
|
+@EnableFeignClients
|
|
|
+@EnableScheduling //开启定时任务
|
|
|
+public class ServiceAlbumApplication implements CommandLineRunner {
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
- SpringApplication.run(ServiceSearchApplication.class, args);
|
|
|
+ SpringApplication.run(ServiceAlbumApplication.class, args);
|
|
|
}
|
|
|
|
|
|
@Autowired
|
|
|
private RedissonClient redissonClient;
|
|
|
|
|
|
+ @PostConstruct
|
|
|
+ public void initBloomFilter() {
|
|
|
+ log.info("模拟初始化布隆过滤器开始...");
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * 当springboot应用启动成功后执行
|
|
|
- *
|
|
|
- * @param args incoming main method arguments
|
|
|
- * @throws Exception
|
|
|
+ * springboot启动后自动执行一次该方法
|
|
|
+ * 项目启动后自动初始化布隆过滤器
|
|
|
*/
|
|
|
@Override
|
|
|
public void run(String... args) throws Exception {
|
|
|
- //初始化布隆过滤器
|
|
|
+ //1.创建/获取布隆过滤器对象
|
|
|
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
- //判断布隆过滤器是否存在
|
|
|
- if (!bloomFilter.isExists()) {
|
|
|
- //通过数据规模+误判率进行初始化布隆过滤器
|
|
|
- bloomFilter.tryInit(100000, 0.03);
|
|
|
+ //2.判断过滤过滤器是否存在
|
|
|
+ boolean exists = bloomFilter.isExists();
|
|
|
+ log.info("布隆过滤器是否存在:{}", exists);
|
|
|
+ if (!exists) {
|
|
|
+ boolean flag = bloomFilter.tryInit(10000L, 0.03);
|
|
|
+ log.info("初始化布隆过滤器,完成结果:{}", flag);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -780,10 +787,11 @@ private RedissonClient redissonClient;
|
|
|
@Override
|
|
|
public void upperAlbum(Long albumId) {
|
|
|
//....省略代码
|
|
|
- //将新增的商品SKUID存入布隆过滤器
|
|
|
- //获取布隆过滤器,将新增skuID存入布隆过滤器
|
|
|
+ //9.将专辑ID存入布隆过滤器
|
|
|
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
- bloomFilter.add(albumInfo.getId());
|
|
|
+ if(bloomFilter.isExists()){
|
|
|
+ bloomFilter.add(albumId);
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
@@ -803,11 +811,11 @@ private RedissonClient redissonClient;
|
|
|
*/
|
|
|
@Override
|
|
|
public Map<String, Object> getAlbumItem(Long albumId) {
|
|
|
- //0.查询布隆过滤器是否包含查询专辑ID
|
|
|
+ //0.采用布隆过滤器解决缓存穿透问题
|
|
|
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
- boolean flag = bloomFilter.contains(albumId);
|
|
|
- if (!flag) {
|
|
|
- throw new GuiguException(404, "访问专辑不存在");
|
|
|
+ boolean contains = bloomFilter.contains(albumId);
|
|
|
+ if(!contains){
|
|
|
+ throw new GuiguException(404, "专辑不存在");
|
|
|
}
|
|
|
//.....省略代码
|
|
|
}
|
|
@@ -818,11 +826,11 @@ public Map<String, Object> getAlbumItem(Long albumId) {
|
|
|
采用定时任务机制进行布隆过滤器扩容
|
|
|
|
|
|
```java
|
|
|
-package com.atguigu.tingshu.album.job;
|
|
|
+package com.atguigu.tingshu.album.task;
|
|
|
|
|
|
-import cn.hutool.core.collection.CollectionUtil;
|
|
|
-import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
|
|
|
+import com.atguigu.tingshu.album.service.AlbumInfoService;
|
|
|
import com.atguigu.tingshu.common.constant.RedisConstant;
|
|
|
+import com.atguigu.tingshu.common.constant.SystemConstant;
|
|
|
import com.atguigu.tingshu.model.album.AlbumInfo;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
@@ -836,47 +844,72 @@ import java.util.List;
|
|
|
|
|
|
/**
|
|
|
* @author: atguigu
|
|
|
- * @create: 2024-08-17 14:10
|
|
|
+ * @create: 2025-06-11 10:19
|
|
|
*/
|
|
|
@Slf4j
|
|
|
@Component
|
|
|
-public class RebuildBloomFilter {
|
|
|
+public class RebuildBloomFilterTask {
|
|
|
+
|
|
|
|
|
|
@Autowired
|
|
|
private RedissonClient redissonClient;
|
|
|
|
|
|
@Autowired
|
|
|
- private AlbumInfoMapper albumInfoMapper;
|
|
|
+ private AlbumInfoService albumInfoService;
|
|
|
|
|
|
/**
|
|
|
- * 每月1号凌晨3点整重建布隆过滤器 cron表达式:0 0 3 * * ?
|
|
|
- * 测试每隔10s进行一次扩容
|
|
|
+ * 公告:每月1号凌晨2~3点项目维护,避免数据不一致
|
|
|
+ * 每月1号2点:重建布隆过滤器
|
|
|
*/
|
|
|
- @Scheduled(cron = "0 0 3 * * ?")
|
|
|
+ @Scheduled(cron = "0 0 2 1 * ?")
|
|
|
+ //@Scheduled(cron = "0/5 * * * * ?")
|
|
|
public void rebuildBloomFilter() {
|
|
|
- log.info("重建布隆过滤器定时任务执行");
|
|
|
- //1.获取"旧"的布隆过滤器-获取现有数据量
|
|
|
+ //1.获取现有布隆过滤器对象,得到配置信息:1.期望数据规模 2.误判率 3.现有元素数量(预估值)
|
|
|
RBloomFilter<Long> oldBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
- //2.判断是否需要进行扩容,扩容条件:现有数量>期望数据规模
|
|
|
- if (oldBloomFilter.count() >= oldBloomFilter.getExpectedInsertions()) {
|
|
|
- //2.1 创建"新"的布隆过滤器完成初始化
|
|
|
+ long expectedInsertions = oldBloomFilter.getExpectedInsertions();
|
|
|
+ double falseProbability = oldBloomFilter.getFalseProbability();
|
|
|
+ long count = oldBloomFilter.count();
|
|
|
+ //2.判断是否满足扩容条件:现有元素数量大于等于期望数据规模
|
|
|
+ if (count >= expectedInsertions) {
|
|
|
+ log.info("满足扩容条件,开始扩容重建...");
|
|
|
+ //2.1 先初始化新的布隆过滤器,原有期望数据规模*2
|
|
|
RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER + ":new");
|
|
|
- newBloomFilter.tryInit(oldBloomFilter.getExpectedInsertions() * 2, oldBloomFilter.getFalseProbability());
|
|
|
- //2.2 删除"旧"的布隆过滤器
|
|
|
- oldBloomFilter.delete();
|
|
|
- //2.3 再对新的布隆进行重命名:改回旧的名称
|
|
|
- newBloomFilter.rename(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
- //3.查询专辑表得到最新库中审核通过的专辑ID列表
|
|
|
- LambdaQueryWrapper<AlbumInfo> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(AlbumInfo::getStatus, "0301");
|
|
|
- queryWrapper.select(AlbumInfo::getId);
|
|
|
- List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(queryWrapper);
|
|
|
- //4.将专辑ID加入到"新"的布隆过滤器
|
|
|
- if (CollectionUtil.isNotEmpty(albumInfoList)) {
|
|
|
- for (AlbumInfo albumInfo : albumInfoList) {
|
|
|
+ if (!newBloomFilter.isExists()) {
|
|
|
+ newBloomFilter.tryInit(expectedInsertions * 2, falseProbability);
|
|
|
+ //2.2 将数据中先有合法专辑ID加入到新的布隆过滤器中
|
|
|
+ List<AlbumInfo> list = albumInfoService.list(
|
|
|
+ new LambdaQueryWrapper<AlbumInfo>()
|
|
|
+ .eq(AlbumInfo::getStatus, SystemConstant.ALBUM_STATUS_PASS)
|
|
|
+ .select(AlbumInfo::getId)
|
|
|
+ );
|
|
|
+ for (AlbumInfo albumInfo : list) {
|
|
|
newBloomFilter.add(albumInfo.getId());
|
|
|
}
|
|
|
+ //2.3 删除旧的布隆过滤器:布隆过滤器配置及位图
|
|
|
+ oldBloomFilter.delete();
|
|
|
+ //2.4 重命名布隆过滤器,改为原来名称
|
|
|
+ newBloomFilter.rename(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
}
|
|
|
+ } else {
|
|
|
+ log.info("不满足扩容条件,开始重建...");
|
|
|
+ //3.不满足扩容条件,做基本重建: 1.初始化布隆过滤器 2.将数据中先有合法专辑ID加入到布隆过滤器中 3.重命名布隆过滤器,改为原来名称 4.删除旧的布隆过滤器
|
|
|
+ //3.1 先初始化新的布隆过滤器
|
|
|
+ RBloomFilter<Long> newBloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER + ":new");
|
|
|
+ newBloomFilter.tryInit(expectedInsertions, falseProbability);
|
|
|
+ //3.2 将数据中先有合法专辑ID加入到新的布隆过滤器中
|
|
|
+ List<AlbumInfo> list = albumInfoService.list(
|
|
|
+ new LambdaQueryWrapper<AlbumInfo>()
|
|
|
+ .eq(AlbumInfo::getStatus, SystemConstant.ALBUM_STATUS_PASS)
|
|
|
+ .select(AlbumInfo::getId)
|
|
|
+ );
|
|
|
+ for (AlbumInfo albumInfo : list) {
|
|
|
+ newBloomFilter.add(albumInfo.getId());
|
|
|
+ }
|
|
|
+ //3.3 删除旧的布隆过滤器:布隆过滤器配置及位图
|
|
|
+ oldBloomFilter.delete();
|
|
|
+ //3.4 重命名布隆过滤器,改为原来名称
|
|
|
+ newBloomFilter.rename(RedisConstant.ALBUM_BLOOM_FILTER);
|
|
|
+ log.info("布隆过滤器重建完成...");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1066,7 +1099,7 @@ FLUSH PRIVILEGES;
|
|
|
> -e canal.instance.filter.regex=.*\\..* 代表监控所有数据库表变更的日志
|
|
|
|
|
|
```shell
|
|
|
- docker run -p 11111:11111 --name canal \
|
|
|
+ docker run -p 11113:11111 --name canal1.5 \
|
|
|
-e canal.destinations=tingshuTopic \
|
|
|
-e canal.instance.master.address=192.168.200.6:3306 \
|
|
|
-e canal.instance.dbUsername=canal \
|
|
@@ -1074,8 +1107,8 @@ FLUSH PRIVILEGES;
|
|
|
-e canal.instance.connectionCharset=UTF-8 \
|
|
|
-e canal.instance.tsdb.enable=true \
|
|
|
-e canal.instance.gtidon=false \
|
|
|
- -e canal.instance.filter.regex=^tingshu_.*\\..* \
|
|
|
- -d canal/canal-server:v1.1.8
|
|
|
+ -e canal.instance.filter.regex=.*\\..* \
|
|
|
+ -d canal/canal-server:v1.1.5
|
|
|
```
|
|
|
|
|
|
4. 删除容器,重新创建canal容器,促使canal重新跟MySQL建立连接,从MySQL最新BinLog偏移量开始读取数据
|
|
@@ -1154,7 +1187,7 @@ FLUSH PRIVILEGES;
|
|
|
|
|
|
3. 在Nacos创建配置文件`service-canal-dev.yaml` 内容如下:
|
|
|
|
|
|
- 
|
|
|
+ 
|
|
|
|
|
|
```yaml
|
|
|
server:
|