自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用 Spring Boot3.3 結(jié)合 Redisson RBloomFilter 有效應(yīng)對(duì)緩存穿透問題

開發(fā) 前端
通過結(jié)合 Spring Boot3.3、Redisson 和 MyBatis-Plus,實(shí)現(xiàn)了使用 RBloomFilter 防止緩存穿透的商品查詢功能。本文提供了詳細(xì)的代碼示例,包括前后端的實(shí)現(xiàn)以及數(shù)據(jù)庫初始化步驟,展示了如何在實(shí)際項(xiàng)目中應(yīng)用布隆過濾器來提高系統(tǒng)性能。

在電商平臺(tái)中,商品查詢是最頻繁的操作之一。隨著商品數(shù)量的增加和用戶訪問量的激增,系統(tǒng)可能面臨頻繁的緩存穿透問題。緩存穿透通常是由惡意請(qǐng)求或用戶錯(cuò)誤輸入導(dǎo)致的:當(dāng)請(qǐng)求的數(shù)據(jù)不存在時(shí),查詢將直接落到數(shù)據(jù)庫上,無法通過緩存系統(tǒng)加速響應(yīng)。這不僅增加了數(shù)據(jù)庫的負(fù)載,還可能導(dǎo)致系統(tǒng)崩潰。在這種情況下,布隆過濾器作為一種高效的概率型數(shù)據(jù)結(jié)構(gòu),可以通過快速判斷某個(gè)數(shù)據(jù)是否存在于集合中,來有效減少無效的數(shù)據(jù)庫查詢,從而解決緩存穿透問題。

布隆過濾器的基本原理是在初始化時(shí),設(shè)置一個(gè)長(zhǎng)度為 m 的位數(shù)組,并通過 k 個(gè)哈希函數(shù)將數(shù)據(jù)映射到位數(shù)組上。當(dāng)有新元素加入時(shí),布隆過濾器會(huì)將該元素的哈希值映射到數(shù)組的 k 個(gè)位置上,并將這些位置的位設(shè)置為 1。在查詢某個(gè)元素是否存在時(shí),布隆過濾器會(huì)通過同樣的 k 個(gè)哈希函數(shù)檢查對(duì)應(yīng)的位是否都為 1,如果有任意一位為 0,則說明該元素不存在;如果所有位都為 1,則說明該元素可能存在。

布隆過濾器的優(yōu)勢(shì)在于它的空間和時(shí)間效率都非常高,適合處理大規(guī)模數(shù)據(jù)的快速查詢。然而,它的缺點(diǎn)是存在一定的誤判率,即可能會(huì)錯(cuò)誤地判斷一個(gè)不存在的元素為存在。為了降低誤判率,布隆過濾器的設(shè)計(jì)需要合理選擇位數(shù)組的長(zhǎng)度 m 和哈希函數(shù)的數(shù)量 k。

在電商平臺(tái)中,我們可以在緩存系統(tǒng)之前使用布隆過濾器,對(duì)查詢請(qǐng)求進(jìn)行預(yù)篩選,以減少對(duì)數(shù)據(jù)庫的直接訪問。本文將通過 Spring Boot3.3 結(jié)合 Redisson 的 RBloomFilter 實(shí)現(xiàn)這一方案,并展示如何通過 MyBatis-Plus 實(shí)現(xiàn)商品數(shù)據(jù)的查詢,同時(shí)結(jié)合前端展示,完整演示該技術(shù)方案的實(shí)現(xiàn)細(xì)節(jié)。

運(yùn)行效果:

有商品

圖片圖片

無商品

圖片圖片

若想獲取項(xiàng)目完整代碼以及其他文章的項(xiàng)目源碼,且在代碼編寫時(shí)遇到問題需要咨詢交流,歡迎加入下方的知識(shí)星球。

項(xiàng)目結(jié)構(gòu)

  • Spring Boot 3.3
  • Redisson
  • Redis
  • MyBatis-Plus
  • Thymeleaf 模板引擎
  • Bootstrap + JS 前端框架

配置項(xiàng)目環(huán)境

Maven 配置 (pom.xml)

在 pom.xml 中引入必要的依賴:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.icoderoad</groupId>
	<artifactId>bloomfilter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>bloomfilter</name>
	<description>BloomFilter Demo project for Spring Boot</description>
	
	<properties>
		<java.version>17</java.version>
		<bootstrap.version>5.3.0</bootstrap.version>
		<jquery.version>3.6.0</jquery.version>
		<mybatis-spring.version>3.0.3</mybatis-spring.version>
		<mybatis-plus-boot-starter.version>3.5.7</mybatis-plus-boot-starter.version>
		<redisson.version>3.35.0</redisson.version>
	</properties>
	<dependencies>
		
		<!-- Spring Boot Starter Web -->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	    </dependency>
	
	     <!-- MyBatis-Plus 依賴 -->
	    <dependency>
	        <groupId>com.baomidou</groupId>
	        <artifactId>mybatis-plus-boot-starter</artifactId>
	        <version>${mybatis-plus-boot-starter.version}</version>
	    </dependency>
	    
	     <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>${mybatis-spring.version}</version>
      	</dependency>
	
	    <!-- MySQL 驅(qū)動(dòng) -->
	    <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
	
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

	    <!-- Redisson -->
	    <dependency>
	        <groupId>org.redisson</groupId>
   			<artifactId>redisson</artifactId>
	        <version>${redisson.version}</version>
	    </dependency>
	
	    <!-- Thymeleaf -->
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-thymeleaf</artifactId>
	    </dependency>
		 
	    <dependency>
	        <groupId>org.projectlombok</groupId>
	        <artifactId>lombok</artifactId>
	        <optional>true</optional>
	    </dependency>
	    
	    <!-- Bootstrap 和 JS -->
	    <dependency>
	        <groupId>org.webjars</groupId>
	        <artifactId>bootstrap</artifactId>
	        <version>${bootstrap.version}</version>
	    </dependency>
	    <dependency>
	        <groupId>org.webjars</groupId>
	        <artifactId>jquery</artifactId>
	        <version>${jquery.version}</version>
	    </dependency>
	    

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>
Yaml 配置 (application.yml)

在 application.yml 中配置 Redis、Redisson 以及 MyBatis-Plus 的相關(guān)信息:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/sensitive?useSSL=false&serverTimeznotallow=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  data:
    redis:
      host: localhost
      port: 6379
      password: 123456
      timeout: 60000
      database: 0
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: -1ms
  thymeleaf:
    cache: false

server:
  port: 8080

商品表創(chuàng)建與數(shù)據(jù)插入

商品表 SQL DDL 語句

首先創(chuàng)建商品表 product,包含商品的基本信息:

CREATE TABLE product (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    price DECIMAL(10, 2) NOT NULL,
    stock INT NOT NULL
);
插入商品數(shù)據(jù)的 SQL 語句

插入20條示例數(shù)據(jù)到 product 表中:

INSERT INTO product (name, description, price, stock) VALUES
('商品1', '這是商品1的描述', 99.99, 100),
('商品2', '這是商品2的描述', 199.99, 50),
('商品3', '這是商品3的描述', 299.99, 150),
('商品4', '這是商品4的描述', 399.99, 200),
('商品5', '這是商品5的描述', 499.99, 10),
('商品6', '這是商品6的描述', 599.99, 5),
('商品7', '這是商品7的描述', 699.99, 300),
('商品8', '這是商品8的描述', 799.99, 400),
('商品9', '這是商品9的描述', 899.99, 500),
('商品10', '這是商品10的描述', 999.99, 600),
('商品11', '這是商品11的描述', 1099.99, 700),
('商品12', '這是商品12的描述', 1199.99, 800),
('商品13', '這是商品13的描述', 1299.99, 900),
('商品14', '這是商品14的描述', 1399.99, 1000),
('商品15', '這是商品15的描述', 1499.99, 1100),
('商品16', '這是商品16的描述', 1599.99, 1200),
('商品17', '這是商品17的描述', 1699.99, 1300),
('商品18', '這是商品18的描述', 1799.99, 1400),
('商品19', '這是商品19的描述', 1899.99, 1500),
('商品20', '這是商品20的描述', 1999.99, 1600);

實(shí)現(xiàn)商品查詢功能

商品實(shí)體類 (Product)
package com.icoderoad.bloomfilter.entity;

import java.math.BigDecimal;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;

@TableName("product")
@Data
public class Product {

    @TableId
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private Integer stock;

}
商品Mapper接口 (ProductMapper)
package com.icoderoad.bloomfilter.mapper;


import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.icoderoad.bloomfilter.entity.Product;

@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
商品服務(wù)接口 (ProductService)
package com.icoderoad.bloomfilter.service;


import com.baomidou.mybatisplus.extension.service.IService;
import com.icoderoad.bloomfilter.entity.Product;

public interface ProductService  extends IService<Product> {
   public Product getProductById(Long id);
}
商品服務(wù)實(shí)現(xiàn)類 (ProductServiceImpl)
package com.icoderoad.bloomfilter.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icoderoad.bloomfilter.entity.Product;
import com.icoderoad.bloomfilter.mapper.ProductMapper;
import com.icoderoad.bloomfilter.service.BloomFilterService;
import com.icoderoad.bloomfilter.service.ProductService;

@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

	 @Autowired
	    private ProductMapper productMapper;

	    @Autowired
	    private BloomFilterService bloomFilterService;

	    @Autowired
	    private RedisTemplate<String, Object> redisTemplate;

	    @Override
	    public Product getProductById(Long id) {
	        String productId = id.toString();

	        // 使用布隆過濾器判斷商品是否存在
	        if (!bloomFilterService.mightContain(productId)) {
	            return null;
	        }

	        // 從 Redis 緩存中獲取數(shù)據(jù)
	        Product product = (Product) redisTemplate.opsForValue().get(productId);
	        if (product == null) {
	            // 如果緩存中沒有,從數(shù)據(jù)庫查詢
	            product = productMapper.selectById(id);
	            if (product != null) {
	                // 將數(shù)據(jù)放入緩存,并添加到布隆過濾器中
	                redisTemplate.opsForValue().set(productId, product);
	                bloomFilterService.addProductToBloomFilter(id);
	            }
	        }
	        return product;
	    }
	    
}

初始化布隆過濾器

BloomFilterService 接口和實(shí)現(xiàn)類

首先,創(chuàng)建 BloomFilterService 接口及其實(shí)現(xiàn)類,用于封裝布隆過濾器的操作:

package com.icoderoad.bloomfilter.service;

public interface BloomFilterService {
	
	void addProductToBloomFilter(Long id);

	boolean mightContain(String id);
}

實(shí)現(xiàn)類:

package com.icoderoad.bloomfilter.service.impl;

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.icoderoad.bloomfilter.service.BloomFilterService;

import jakarta.annotation.PostConstruct;

@Service
public class BloomFilterServiceImpl implements BloomFilterService {

    @Autowired
    private RedissonClient redissonClient;

    private RBloomFilter<String> bloomFilter;

    // 初始化布隆過濾器
    @PostConstruct
    public void init() {
    	if( redissonClient!=null ) {
	        this.bloomFilter = redissonClient.getBloomFilter("productBloomFilter");
	        // 初始化布隆過濾器的大小和誤判率
	        this.bloomFilter.tryInit(1000000L, 0.01);
    	}
    }

    @Override
    public void addProductToBloomFilter(Long id) {
        bloomFilter.add(id.toString());
    }

    @Override
    public boolean mightContain(String id) {
        return bloomFilter.contains(id);
    }
}

創(chuàng)建 RedissonConfig 配置類

屬性類 RedisProperties:

package com.icoderoad.bloomfilter.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import lombok.Data;

@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
@Data
public class RedisProperties {

    private String host;
    private int port;
    private String password;
    private int timeout;
    private int database;

}

創(chuàng)建 RedissonConfig 配置類,從 application.yml 中讀取 Redis 配置:

package com.icoderoad.bloomfilter.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedissonConfig {
	
	@Autowired
    private RedisProperties redisProperties;
	
	@Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
	
	
    @Bean("redissonClient")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
              .setPassword(redisProperties.getPassword())
              .setConnectionPoolSize(10)
              .setConnectionMinimumIdleSize(5)
              .setTimeout(redisProperties.getTimeout())
              .setDatabase(redisProperties.getDatabase());

        return Redisson.create(config);
    }

}

說明

  • @Value 注解用于從 application.yml 中讀取配置屬性。
  • setAddress 方法中使用 redisHost 和 redisPort 構(gòu)建 Redis 地址。
  • 其他 Redis 配置如密碼、連接超時(shí)、數(shù)據(jù)庫索引等,也從配置文件中讀取。

在啟動(dòng)時(shí)初始化 BloomFilter (ApplicationRunner)

然后,在項(xiàng)目啟動(dòng)時(shí)將商品數(shù)據(jù)添加到 BloomFilter 中:

package com.icoderoad.bloomfilter.init;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import com.icoderoad.bloomfilter.entity.Product;
import com.icoderoad.bloomfilter.mapper.ProductMapper;
import com.icoderoad.bloomfilter.service.BloomFilterService;

@Component
public class BloomFilterInitializer {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private BloomFilterService bloomFilterService;

    @Bean
    public ApplicationRunner initializeBloomFilter() {
        return args -> {
            // 從數(shù)據(jù)庫中獲取所有商品
            List<Product> products = productMapper.selectList(null);
            // 將每個(gè)商品的ID添加到布隆過濾器中
            for (Product product : products) {
                bloomFilterService.addProductToBloomFilter(product.getId());
            }
        };
    }
}

這個(gè) ApplicationRunner 會(huì)在 Spring Boot 應(yīng)用啟動(dòng)時(shí)運(yùn)行,遍歷數(shù)據(jù)庫中的所有商品,并將它們的 ID 添加到布隆過濾器中。這確保了在系統(tǒng)啟動(dòng)時(shí),布隆過濾器已經(jīng)初始化,并且包含所有現(xiàn)有商品的數(shù)據(jù)。

前端展示

HTML 頁面 (Thymeleaf 模板)

使用 Thymeleaf 結(jié)合 Bootstrap 實(shí)現(xiàn)前端頁面,允許用戶輸入商品ID并查詢商品信息:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>商品查詢</title>
    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container">
    <h1>商品查詢</h1>
    <form id="searchForm" th:action="@{/product/search}" method="get">
        <div class="form-group">
            <label for="productId">商品ID:</label>
            <input type="text" class="form-control" id="productId" name="productId" required>
        </div>
        <button type="submit" class="btn btn-primary">查詢</button>
    </form>

    <div id="productResult" th:if="${product != null}">
        <h2>商品信息</h2>
        <p>ID: <span th:text="${product.id}"></span></p>
        <p>名稱: <span th:text="${product.name}"></span></p>
        <p>描述: <span th:text="${product.description}"></span></p>
        <p>價(jià)格: <span th:text="${product.price}"></span></p>
        <p>庫存: <span th:text="${product.stock}"></span></p>
    </div>
</div>
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/bootstrap/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Controller 實(shí)現(xiàn)
package com.icoderoad.bloomfilter.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.icoderoad.bloomfilter.entity.Product;
import com.icoderoad.bloomfilter.service.ProductService;

@Controller
public class ProductController {

    @Autowired
    private ProductService productService;
    
    @GetMapping("/")
    public String index(Model model) {
        return "index";
    }

    @GetMapping("/product/search")
    public String searchProduct(@RequestParam("productId") Long productId, Model model) {
        Product product = productService.getProductById(productId);
        model.addAttribute("product", product);
        return "index";
    }
}

結(jié)論

通過結(jié)合 Spring Boot3.3、Redisson 和 MyBatis-Plus,實(shí)現(xiàn)了使用 RBloomFilter 防止緩存穿透的商品查詢功能。本文提供了詳細(xì)的代碼示例,包括前后端的實(shí)現(xiàn)以及數(shù)據(jù)庫初始化步驟,展示了如何在實(shí)際項(xiàng)目中應(yīng)用布隆過濾器來提高系統(tǒng)性能。

今天就講到這里,如果有問題需要咨詢,大家可以直接留言或掃下方二維碼來知識(shí)星球找我,我們會(huì)盡力為你解答。

責(zé)任編輯:武曉燕 來源: 碼猿技術(shù)專欄
相關(guān)推薦

2024-09-26 09:28:06

內(nèi)存Spring

2024-09-02 08:12:32

Spring策略MyBatis

2025-02-20 08:21:36

2017-12-28 22:22:05

2009-03-09 11:01:34

2024-08-29 08:23:22

EasyOCRSpring文字識(shí)別

2025-04-17 03:33:00

SpringSQL動(dòng)態(tài)查詢

2024-10-14 13:26:42

2015-12-28 10:48:44

RedisSpring緩存實(shí)例

2016-07-15 18:10:16

Intel數(shù)據(jù)中心數(shù)據(jù)中心管理器

2021-12-02 13:56:33

勒索軟件攻擊網(wǎng)絡(luò)安全

2024-12-09 13:50:24

2024-03-06 13:29:15

人工智能雙碳能源管理

2023-10-04 19:43:38

2024-08-27 09:28:39

2024-09-05 09:35:58

CGLIBSpring動(dòng)態(tài)代理

2023-01-31 08:37:11

緩存穿透擊穿

2021-10-21 09:56:18

人工智能金融行業(yè)勒索軟件

2025-02-21 12:00:00

SpringBoot防重復(fù)提交緩存機(jī)制

2024-04-29 18:55:16

緩存Spring性能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)