国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢

這篇具有很好參考價值的文章主要介紹了【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

正式觀看本文之前,設想一個問題,高并發(fā)情況下,首頁列表數(shù)據(jù)怎么做?

【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢

類似淘寶首頁,這些商品是從數(shù)據(jù)庫中查出來的嗎?答案肯定不是,在高并發(fā)的情況下,數(shù)據(jù)庫是扛不住的,那么我們要怎么去扛住C端大并發(fā)量呢,這塊我們可以借助Redis,我們知道Redis是一個基于內(nèi)存的NoSQL數(shù)據(jù)庫。學過操作系統(tǒng)我們都知道,內(nèi)存要比磁盤的效率大的多,那Redis就是基于內(nèi)存的,而數(shù)據(jù)庫是基于磁盤的。

還有類似天貓聚劃算商品類表。
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢

我們現(xiàn)在知道要用Redis去做首頁數(shù)據(jù)的分頁,那么我們應該用Redis的那種數(shù)據(jù)結構來做呢。

Redis有5種基本的數(shù)據(jù)結構,我們這里用list類型做分頁。

在 Redis 中,List(列表)類型是按照元素的插入順序排序的字符串列表。你可以在列表的頭部(左邊)或者尾部(右部)添加新的元素。

ok,那么接下來我們就通過一個案例實操一下,首頁熱點數(shù)據(jù)怎么放到Redis中去查詢。

SpringBoot整合RedisTemplate這里就不做過多介紹啦,大家可以網(wǎng)上找篇博文 整合一下。

<!-- 創(chuàng)建SpringBoot項目加入redis的starter依賴 -->
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

編寫ProductService,定于數(shù)據(jù)分頁方法。

public interface ProductService {

    Map<String,Object> productListPage(int current, int size) throws InterruptedException;

}

編寫ProductServiceImpl實現(xiàn)類。

/**
 * @author lixiang
 * @date 2023/6/18 21:01
 */
@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    private static final String PRODUCT_LIST_KEY = "product:list";

    private static final List<Product> PRODUCT_LIST;

    //模擬從數(shù)據(jù)庫中查出來的數(shù)據(jù)
    static {
        PRODUCT_LIST = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            Product product = new Product();
            product.setId(UUID.randomUUID().toString().replace("-", ""));
            product.setName("商品名稱:" + i);
            product.setDesc("商品描述:" + i);
            product.setPrice(new BigDecimal(i));
            product.setInventory(2);
            PRODUCT_LIST.add(product);
        }
    }

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public Map<String, Object> productListPage(int current, int size) throws InterruptedException {

        //從緩存中拿到分頁數(shù)據(jù)
        List<Product> productList = getProductListByRedis(current, size);

        if (productList == null || productList.size() == 0) {
            log.info("當前緩存中無分頁數(shù)據(jù),當前頁:" + current + ",頁大小:" + size);
            //從數(shù)據(jù)庫中拿到分頁數(shù)據(jù)
            productList = getProductListByDataSource(current, size);
        }
        Map<String, Object> resultMap = new HashMap<>();
        //計算當前總頁數(shù)
        int totalPage = (PRODUCT_LIST.size() + size - 1) / size;
        resultMap.put("total", PRODUCT_LIST.size());
        resultMap.put("data", productList);
        resultMap.put("pages", totalPage);
        return resultMap;
    }

    private List<Product> getProductListByRedis(int current, int size) {
        log.info("從Redis取出商品信息列表,當前頁:" + current + ",頁大小:" + size);
        // 計算總頁數(shù)
        int pages = pages(size);
        // 起始位置
        int start = current <= 0 ? 0 : (current > pages ? (pages - 1) * size : (current - 1) * size);
        // 終止位置
        int end = start+size-1;
        List<Product> list = redisTemplate.opsForList().range(PRODUCT_LIST_KEY, start, end);
        List<Product> productList = list;
        return productList;
    }

    /**
     * 獲取商品信息集合
     *
     * @return
     */
    private List<Product> getProductListByDataSource(int current, int size) throws InterruptedException {
        //模擬從DB查詢需要300ms
        Thread.sleep(300);
        log.info("從數(shù)據(jù)庫取出商品信息列表,當前頁:" + current + ",頁大小:" + size);
        // 計算總頁數(shù)
        int pages = pages(size);
        // 起始位置
        int start = current <= 0 ? 0 : (current > pages ? (pages - 1) * size : (current - 1) * size);
        //數(shù)據(jù)緩存到redis中
        redisTemplate.opsForList().rightPushAll(PRODUCT_LIST_KEY, PRODUCT_LIST);
        //設置當前key過期時間為1個小時
        redisTemplate.expire(PRODUCT_LIST_KEY,1000*60*60, TimeUnit.MILLISECONDS);
        return PRODUCT_LIST.stream().skip(start).limit(size).collect(Collectors.toList());
    }

    /**
     *  獲取總頁數(shù)
     * @param size
     * @return
     */
    private Integer pages(int size){
        int pages = PRODUCT_LIST.size() % size == 0 ? PRODUCT_LIST.size() / size : PRODUCT_LIST.size() / size + 1;
        return pages;
    }
}

ok,然后編寫controller,進行測試。

@RestController
@RequestMapping("/api/v1/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/page")
    public Map<String,Object> page(@RequestParam("current") int current,@RequestParam("size") int size){
        Map<String, Object> stringObjectMap;
        try {
            stringObjectMap = productService.productListPage(current, size);
        } catch (InterruptedException e) {
            stringObjectMap = new HashMap<>();
        }
        return stringObjectMap;
    }
}

當?shù)谝淮卧L問的時候,先去Redis中查詢,發(fā)現(xiàn)沒有,然后就去查DB,將要緩存的數(shù)據(jù)頁放到Redis中。
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢

第二次訪問的時候。就直接訪問Redis啦

【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢
【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢

通過Redis和DB查詢的對比,我們發(fā)現(xiàn)從Redis中拿出來只用了18ms,從公DB中需要300ms,由此可見Redis的一個強大之處。

那么我們觀察一下查詢邏輯,會不會有什么問題。

    public Map<String, Object> productListPage(int current, int size) throws InterruptedException {

        //從緩存中拿到分頁數(shù)據(jù)
        List<Product> productList = getProductListByRedis(current, size);

        if (productList == null || productList.size() == 0) {
            log.info("當前緩存中無分頁數(shù)據(jù),當前頁:" + current + ",頁大小:" + size);
            //從數(shù)據(jù)庫中拿到分頁數(shù)據(jù)
            productList = getProductListByDataSource(current, size);
        }
    }

設想,假如某一時刻,Redis中的緩存失效啦,大量的請求,全部查到DB上,也會帶來一個災難。所以這快又涉及到一個緩存擊穿的問題。

解決緩存擊穿

  • 方案一:永不過期
    • 提前把熱點數(shù)據(jù)不設置過期時間,后臺異步更新緩存。
  • 方案二:加互斥鎖或隊列
    • 其實我理解緩存擊穿和緩存穿透差不多,所以加一個互斥鎖,讓一個線程正常請求數(shù)據(jù)庫,其他線程等待即可(這里可以使用線程池來處理),都創(chuàng)建完緩存,讓其他線程請求緩存即可。

在這里我們采用第一種方式,讓key永遠不過期。

那可能有的人會說了,這很簡單啊,那我就設置一個定時任務定時的去刷新key就可以了啊。于是寫出了如下的定時作業(yè)代碼。

// 定時任務,每隔30分鐘,從數(shù)據(jù)庫中讀取商品列表,存儲到緩存里面
priviate static final String PRODUCT_LIST_KEY = "product:list";

@Scheduled(cron = "0 */30 * * * ?")
public void loadActivityProduct() {
  //從數(shù)據(jù)庫中查詢參加活動的商品列表
  List<Product> productList = productMapper.queryAcitvityProductList();
  //刪除舊的
  redisTemplate.delete(PRODUCT_LIST_KEY);
  //存儲新的
  redis.opsForList.leftPushAll(PRODUCT_LIST_KEY, productList)
}

但是,不知道大家有沒發(fā)現(xiàn),我們即使加了定時任務的代碼也會發(fā)生緩存擊穿的問題。因為刪除舊的數(shù)據(jù) 和 存儲新的數(shù)據(jù)兩個命令非原子操作,存在時間間隔。如果改用string結構存儲,可以直接覆蓋舊值,則沒有原子性問題,但是業(yè)務需求需要支持分頁,只能用list結構。

	//就在我刪除舊的key的時候,這會還沒有往redis中放入,大的并發(fā)量進來導致請求都跑到了數(shù)據(jù)庫上,造成緩存擊穿。
	//刪除舊的
  redisTemplate.delete(PRODUCT_LIST_KEY);
  //存儲新的
  redis.opsForList.leftPushAll(PRODUCT_LIST_KEY, productList)

解決方案

  • 業(yè)務架構里面強調降級,兜底數(shù)據(jù),那緩存擊穿是不是也可以考慮這個方案,空間換時間

  • 緩存兩份數(shù)據(jù),一份是List結構(先刪除,再設置新值), 一份是String結構(直接覆蓋舊值)

    • 查詢的時候優(yōu)先查詢list結構,如果沒有則解析String結構成為list,進行內(nèi)存分頁,一般數(shù)據(jù)量不大
// 定時任務,每隔30分鐘,從數(shù)據(jù)庫中讀取商品列表,存儲到緩存里面
priviate static final String PRODUCT_LIST_KEY = "product:list";
priviate static final String PRODUCT_LIST_KEY_STR = "product:liststr";

@Scheduled(cron = "0 */30 * * * ?")
public void loadActivityProduct() {
  //從數(shù)據(jù)庫中查詢參加活動的商品列表
  List<Product> productList = productMapper.queryAcitvityProductList();
  
  //先緩存一份String類型的數(shù)據(jù),直接set,如果要分頁則解析成list再返回
  redis.opsForValue.set(PRODUCT_LIST_KEY_STR, JSON.toString(productList))
  
  //刪除舊的
  redisTemplate.delete(PRODUCT_LIST_KEY);
  //存儲新的
  redis.opsForList.leftPushAll(PRODUCT_LIST_KEY, productList)
}

查詢的時候,先去查list結構,list結構如果沒有數(shù)據(jù),則查String類型的數(shù)據(jù)。

priviate static final String PRODUCT_LIST_KEY = "product:list";
priviate static final String PRODUCT_LIST_KEY_STR = "product:liststr";

// 將商品列表從 Redis 緩存中讀取
public List<Product> getProductListFromCache(int begin, int end) {
    List<Product> list = new ArrayList();

    //從緩存里分頁獲取
    list = redisTemplate.opsForList().range(PRODUCT_LIST_KEY, begin,end)
    if (productListStr != null) {
      return list;
    } else {
        // 緩存A中不存在商品列表,則從緩存B讀取
        String productStrList = redis.opsForValue.get(PRODUCT_LIST_KEY_STR);
        // 緩存中存在商品列表,將 JSON 字符串轉換為對象
        List<Product> productList = JSON.parseArray(productStrList, Product.class);
        //分頁計算
        list = CommonUtil.pageList(productList,begin, end);
       return list;
    }
}

OK,整篇的案例整合 我們就到這里,覺得博主寫的不錯的,記得給個三連哦?。。?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-491649.html

【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢文章來源地址http://www.zghlxwxcb.cn/news/detail-491649.html

到了這里,關于【案例實戰(zhàn)】SpringBoot整合Redis實現(xiàn)緩存分頁數(shù)據(jù)查詢的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • SpringBoot 整合 Redis 緩存

    Spring Boot提供了對Spring Cache抽象的支持,可以很容易地與Redis集成。 在pom.xml文件中添加Spring Boot Starter Redis依賴: 在application.properties或application.yml中配置Redis連接信息: 在Spring Boot應用的主類(通常是帶有@SpringBootApplication注解的類)上添加@EnableCaching注解,啟用緩存支持: 在

    2024年01月16日
    瀏覽(35)
  • Redis實戰(zhàn)案例4-緩存更新策略

    Redis實戰(zhàn)案例4-緩存更新策略

    緩存中的數(shù)據(jù)一致性問題(數(shù)據(jù)庫更新數(shù)據(jù),而Redis存的是舊數(shù)據(jù)) 內(nèi)存淘汰策略:當內(nèi)存很充足時,很長時間無法淘汰數(shù)據(jù),所以很難控制淘汰,一致性差; 超時剔除:取決于TTL大小,可以達到控制目的,但是在TTL時間內(nèi)也可能存在數(shù)據(jù)庫更新從而Redis中變成舊數(shù)據(jù); 主動

    2024年02月10日
    瀏覽(44)
  • SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    SpringBoot整合Redis、以及緩存穿透、緩存雪崩、緩存擊穿的理解分布式情況下如何添加分布式鎖 【續(xù)篇】

    上一篇實現(xiàn)了單體應用下如何上鎖,這一篇主要說明如何在分布式場景下上鎖 上一篇地址:加鎖 需要注意的點是: 在上鎖和釋放鎖的過程中要保證 原子性操作 核心是上鎖和解鎖的過程 關于解鎖使用腳本參考:SET key value [EX seconds] [PX milliseconds] [NX|XX] 3.1 一個服務按照多個端口同時

    2023年04月10日
    瀏覽(29)
  • Springboot整合Redis集群實戰(zhàn)詳解

    Springboot整合Redis集群實戰(zhàn)詳解

    Springboot 整合 Redis 集群,實現(xiàn) Redis 分布式方案詳解 前言 準備工作 Redis 集群環(huán)境搭建 Redis 集群故障轉移(主從復制) Redis 集群擴展與收縮節(jié)點 Redis 集群擴展節(jié)點(添加節(jié)點) Redis 集群收縮節(jié)點(移除節(jié)點) Springboot 整合 Redis 集群 Redis Sentinel安裝與部署,實現(xiàn)redis的高可用

    2024年02月09日
    瀏覽(21)
  • mall整合Redis實現(xiàn)緩存功能

    mall整合Redis實現(xiàn)緩存功能

    本文主要講解mall整合Redis的過程,以短信驗證碼的存儲驗證為例。 Redis是用C語言開發(fā)的一個高性能鍵值對數(shù)據(jù)庫,可用于數(shù)據(jù)緩存,主要用于處理大量數(shù)據(jù)的高訪問負載。 下載Redis,下載地址:github.com/MicrosoftAr… 下載完后解壓到指定目錄 在當前地址欄輸入cmd后,執(zhí)行redis的啟

    2024年01月19日
    瀏覽(26)
  • SpringBoot整合ElasticSearch實現(xiàn)分頁查詢

    SpringBoot整合ElasticSearch實現(xiàn)分頁查詢

    本文使用SpringBoot整合ElasticSearch實現(xiàn)分頁查詢 還是繼續(xù)使用spring-boot-starter-data-elasticsearch來實現(xiàn)分頁查詢操作 數(shù)據(jù)準備 使用ElasticsearchRestTemplate來實現(xiàn) 程序結果 使用ElasticsearchOperations來實現(xiàn) 程序結果 本文記錄了SpringBoot整合ElasticSearch來實現(xiàn)分頁查詢的兩種方式

    2024年01月25日
    瀏覽(19)
  • 【案例實戰(zhàn)】SpringBoot整合阿里云文件上傳OSS

    【案例實戰(zhàn)】SpringBoot整合阿里云文件上傳OSS

    1.需求背景 C端業(yè)務用戶頭像上傳 海量圖片音頻、視頻存儲 用戶行為日志存儲 (1)阿里云OSS介紹 對象存儲OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存儲服務。其數(shù)據(jù)設計持久性不低于99.9999999999%(12個9),服務設計可用性不低于99.995%。 OSS具

    2024年02月06日
    瀏覽(26)
  • java springboot整合MyBatis實現(xiàn)分頁查詢以及帶條件的分頁查詢

    java springboot整合MyBatis實現(xiàn)分頁查詢以及帶條件的分頁查詢

    之前的文章 java springboot整合MyBatis做數(shù)據(jù)庫查詢操作操作了springboot整合MyBatis,然后簡單做了個按id查詢的操作 那么 我們按上文搭建起的環(huán)境繼續(xù) 我們直接在staffDao接口中聲明一個分頁函數(shù) 這里 我們直接在 sql語句中寫limit 分頁邏輯 參數(shù)是方法接收的 這個函數(shù)接收兩個參數(shù)

    2024年02月10日
    瀏覽(22)
  • SpringBoot 整合ElasticSearch實現(xiàn)模糊查詢,批量CRUD,排序,分頁,高亮

    SpringBoot 整合ElasticSearch實現(xiàn)模糊查詢,批量CRUD,排序,分頁,高亮

    準備一個空的SpringBoot項目 寫入依賴 注意你的SpringBoot和你的es版本,一定要對應,如果不知道的可以查看這篇文章:https://blog.csdn.net/u014641168/article/details/130386872 我的版本是2.2.6,所以用的ES版本是 6.8.12,安裝es請看這篇文章:https://blog.csdn.net/u014641168/article/details/130622430 查看

    2024年02月08日
    瀏覽(41)
  • RabbitMq整合Springboot超全實戰(zhàn)案例+圖文演示+源碼自取

    RabbitMq整合Springboot超全實戰(zhàn)案例+圖文演示+源碼自取

    目錄 介紹 簡單整合 簡單模式 定義 代碼示例 ?work模式 定義 代碼示例 pubsub模式 定義 代碼示例 routing模式? 定義 代碼示例 ?top模式 定義 代碼 下單付款加積分示例 介紹 代碼 ?可靠性投遞示例 介紹 代碼 交換機投遞確認回調? 隊列投遞確認回調? ?延遲消息場景示例 介紹 代

    2024年02月03日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包