成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis 分頁 + 多條件模糊查詢太頭疼?這套方案幫你輕松搞定!

數據庫 Redis
根據不同的業務場景選擇合適的數據結構,比如有序集合適合需要排序和范圍查詢的場景,集合適合需要去重和交集、并集操作的場景,字符串適合存儲單個對象的詳細信息。

我猜不少搞 Java 開發的兄弟,在項目里碰到 Redis 分頁和多條件模糊查詢的時候,都跟我一樣,心里直犯嘀咕:"這玩意兒咋整啊?咋就這么難搞呢?" 別慌,今兒個咱就來好好嘮嘮,怎么把這倆難題輕松搞定,讓你在同事面前狠狠露一手!

一、先搞明白為啥 Redis 分頁和多條件模糊查詢讓人頭大

咱先說說 Redis 分頁。用過 Redis 的都知道,它和咱們熟悉的 MySQL 這些關系型數據庫不一樣。MySQL 里有個 LIMIT 關鍵字,分頁查詢那叫一個方便,直接就能指定查第幾頁、每頁多少條。可 Redis 呢,它主要是基于內存的鍵值對存儲,數據結構雖然豐富,但原生就沒有像數據庫那樣專門的分頁功能。

你要是存的數據是放在列表(List)里,想分頁的話,可能得用 LRANGE 命令。比如說列表鍵是 users,想查第 1 頁,每頁 10 條,就用 LRANGE users 0 9。乍一看好像還行,可要是列表里的數據是動態變化的,比如經常有新增、刪除操作,列表里元素的位置就會變,這時候用 LRANGE 分頁,結果可能就不準確了。而且要是列表特別大,每次用 LRANGE 都得遍歷一堆元素,性能也會受影響。

再看看多條件模糊查詢。Redis 本身的查詢能力比較有限,不像數據庫能支持復雜的 SQL 語句,什么 LIKE 啊、多個條件組合啊都能輕松搞定。Redis 里的鍵匹配,一般就靠 KEYS 命令或者 SCAN 命令。KEYS 命令能根據通配符匹配鍵,比如 KEYS user:* 能查出所有以 user: 開頭的鍵,可這玩意兒有個大問題,它是全量掃描,在生產環境用的話,要是鍵的數量特別多,會把 Redis 搞得很慢,甚至卡住。

SCAN 命令雖然能增量掃描,避免全量掃描的問題,但它返回的只是鍵,要是你想根據鍵對應的值里的多個條件進行模糊查詢,比如用戶表里要根據用戶名包含 "張三",年齡在 20 到 30 之間來查詢,SCAN 就沒辦法直接做到了,你得把鍵對應的所有值都取出來,在應用層進行過濾,這就會增加應用服務器的負擔,而且效率也不高。

舉個簡單的例子,假設咱們有個電商項目,要在 Redis 里存儲商品信息,每個商品的鍵是 product:1、product:2 這樣的形式,值是 JSON 格式,包含商品名稱、價格、類別等信息。現在要查詢名稱里包含 "手機",價格在 2000 到 4000 之間的商品,并且要分頁顯示。這時候問題就來了,怎么根據商品名稱和價格這兩個條件來查詢呢?直接用 Redis 原生的功能很難實現,這就需要咱們想辦法來解決。

二、搞定 Redis 分頁的實用方案

(一)基于有序集合(Sorted Set)的分頁方案

有序集合是 Redis 里一個很強大的數據結構,它每個元素都有一個分數(score),可以根據分數對元素進行排序。咱們可以利用這個特性來實現分頁。

比如說,咱們還是以用戶數據為例,每個用戶有一個唯一的 ID,咱們可以把用戶 ID 作為有序集合的成員,把用戶的創建時間作為分數。這樣有序集合里的元素就是按照創建時間排序的。

要實現分頁查詢,假設每頁顯示 n 條數據,第 m 頁的起始索引就是 (m - 1) * n,結束索引就是 m * n - 1。然后用 ZRANGE 命令來獲取指定范圍內的成員。比如有序集合鍵是 users_sorted,查第 1 頁,每頁 10 條,就是 ZRANGE users_sorted 0 9。

但是這里有個問題,如果用戶數據是不斷更新的,比如有用戶刪除了,有序集合里的元素數量會減少,這時候原來的索引就會發生變化。不過對于大部分分頁場景來說,只要不是頻繁刪除中間的元素,這種方案還是比較可行的。

(二)記錄上一頁最后一個元素的分頁方案

這種方案適合數據是按照一定順序排列的情況,比如時間順序。咱們在查詢上一頁數據的時候,記錄下最后一個元素的相關信息,比如時間戳或者 ID,然后在下一頁查詢時,根據這個信息來獲取下一頁的數據。

比如,咱們還是以按創建時間排序的用戶數據為例,假設上一頁最后一個用戶的創建時間是 last_score,那么下一頁查詢的時候,就可以用 ZRANGEBYSCORE 命令,從 last_score 之后開始獲取數據。命令大概是這樣的:ZRANGEBYSCORE users_sorted (last_score 0 9,這里的 (last_score 表示不包含 last_score 這個分數的元素,然后獲取 10 條數據。

這種方案的好處是可以避免因為中間元素刪除導致索引變化的問題,而且每次查詢的時間復雜度比較低,適合大數據量的分頁場景。

三、解決 Redis 多條件模糊查詢的巧妙辦法

(一)預處理數據,建立多個索引

既然 Redis 原生不支持多條件模糊查詢,那咱們可以在數據寫入 Redis 的時候,對數據進行預處理,根據不同的查詢條件建立索引。

還是以電商商品為例,商品有名稱、價格、類別等屬性。咱們可以建立三個有序集合:

  • 以商品名稱為索引的有序集合 product_name_index,成員是商品 ID,分數可以是商品名稱的某種哈希值或者直接是名稱的拼音首字母(方便模糊查詢)。
  • 以價格為索引的有序集合 product_price_index,成員是商品 ID,分數就是商品的價格。
  • 以類別為索引的有序集合 product_category_index,成員是商品 ID,分數可以是類別 ID。

當要進行多條件模糊查詢時,比如查詢名稱包含 "手機",價格在 2000 到 4000 之間的商品,咱們可以先根據名稱條件,從 product_name_index 中獲取所有名稱包含 "手機" 的商品 ID 集合,再從 product_price_index 中獲取價格在 2000 到 4000 之間的商品 ID 集合,然后對這兩個集合取交集,得到同時滿足這兩個條件的商品 ID,最后根據這些商品 ID 去獲取具體的商品信息。

對于模糊查詢名稱包含 "手機",咱們可以在建立索引的時候,把商品名稱的所有可能的子串都作為索引的一部分,或者使用一些模糊匹配的算法,比如編輯距離算法,不過這可能會增加索引的存儲量。更簡單的辦法是,在應用層對輸入的模糊查詢關鍵詞進行處理,生成對應的通配符模式,然后在 Redis 中使用 SCAN 命令結合鍵的模式來獲取相關的索引鍵,再獲取對應的商品 ID 集合。

(二)使用 Redis 的位圖(Bitmap)

位圖可以用來表示某個元素是否存在,或者某個條件是否滿足。比如對于每個商品,我們可以用不同的位圖來表示不同的條件,比如價格是否在某個區間,類別是否屬于某一類等。

不過位圖在多條件查詢中的應用相對比較復雜,需要結合其他數據結構一起使用,這里咱們先重點介紹前面的索引方案。

四、綜合方案:讓分頁和多條件模糊查詢無縫結合

現在咱們把分頁和多條件模糊查詢結合起來,看看怎么在實際場景中應用。

假設咱們還是那個電商項目,要實現根據商品名稱模糊查詢、價格范圍查詢,并且進行分頁顯示的功能。具體步驟如下:

(一)數據寫入階段

  1. 當新增一個商品時,首先生成一個唯一的商品 ID,比如 product:1001。
  2. 將商品的詳細信息以 JSON 格式存儲在 Redis 的字符串鍵中,鍵為 product:1001,值為 {"name":"華為手機", "price":3000, "category":"電子產品", "other_info":"..."}。
  3. 建立名稱索引:將商品名稱進行處理,比如提取所有可能包含的關鍵詞,這里假設我們簡單地將整個名稱作為索引的一部分,在有序集合 product_name_index 中,以商品 ID 為成員,以名稱的拼音或者某種可以用于模糊查詢的標識為分數(這里為了方便,暫時以名稱本身作為分數,實際項目中可能需要更復雜的處理)。比如 ZADD product_name_index "華為手機" "product:1001"。
  4. 建立價格索引:在有序集合 product_price_index 中,以商品 ID 為成員,價格為分數,執行 ZADD product_price_index 3000 "product:1001"。
  5. 建立類別索引:在有序集合 product_category_index 中,以商品 ID 為成員,類別 ID 或者類別名稱為分數,假設類別是 "電子產品",執行 ZADD product_category_index "電子產品" "product:1001"。

(二)查詢階段

當用戶輸入查詢條件,比如名稱包含 "手機",價格在 2000 到 4000 之間,要查詢第 2 頁,每頁 10 條數據時:

  1. 處理名稱模糊查詢:生成名稱的通配符模式,比如 "手機",然后使用 SCAN 命令在 product_name_index 中查找所有分數包含 "手機" 的成員(這里需要注意,SCAN 命令本身不能直接根據分數的內容進行模糊查詢,所以前面的索引建立方式可能需要調整,更合理的做法是將商品名稱的關鍵詞提取出來,作為有序集合的成員,分數作為商品 ID,或者使用其他數據結構來存儲關鍵詞和商品 ID 的映射關系。這里為了方便演示,假設我們有一個鍵為 name:手機 的集合,里面存儲了所有名稱包含 "手機" 的商品 ID)。
  2. 獲取價格在 2000 到 4000 之間的商品 ID 集合,使用 ZRANGEBYSCORE product_price_index 2000 4000。
  3. 對這兩個集合取交集,得到同時滿足名稱和價格條件的商品 ID 集合,可以使用 Redis 的 ZINTERSTORE 命令,將兩個有序集合的交集存儲到一個臨時有序集合中。
  4. 對臨時有序集合進行分頁查詢,假設我們要按價格排序(也可以按其他條件排序),使用 ZRANGE 命令,根據頁碼和每頁數量計算出起始和結束索引,比如第 2 頁,每頁 10 條,起始索引是 10,結束索引是 19,執行 ZRANGE temp_index 10 19,得到該頁的商品 ID。
  5. 根據商品 ID 從對應的字符串鍵中獲取商品的詳細信息,返回給用戶。

(三)代碼示例(Java 版本)

這里使用 Jedis 客戶端來演示部分代碼:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.*;
public class RedisQueryDemo {
    private Jedis jedis;
    public RedisQueryDemo() {
        jedis = new Jedis("localhost", 6379);
    }
    // 寫入商品數據并建立索引
    public void addProduct(String productId, String name, double price, String category) {
        // 存儲商品詳情
        String productKey = "product:" + productId;
        String productInfo = String.format("{\"name\":\"%s\", \"price\":%f, \"category\":\"%s\"}", name, price, category);
        jedis.set(productKey, productInfo);
        // 建立名稱索引(這里簡化處理,實際可能需要更復雜的關鍵詞提取)
        jedis.zadd("product_name_index", 0, name + ":" + productId); // 這里分數設為 0,僅作為存儲成員的方式,實際可根據需求設置
        // 建立價格索引
        jedis.zadd("product_price_index", price, productId);
        // 建立類別索引
        jedis.zadd("product_category_index", 0, category + ":" + productId); // 同理,分數設為 0
    }
    // 多條件模糊查詢并分頁
    public List<String> searchProducts(String nameKeyword, double minPrice, double maxPrice, int page, int pageSize) {
        List<String> resultProductIds = new ArrayList<>();
        // 獲取名稱包含關鍵詞的商品 ID 集合(簡化處理,實際需根據關鍵詞生成通配符并掃描)
        Set<String> nameMatchedProducts = new HashSet<>();
        // 這里模擬通過關鍵詞獲取相關成員,實際可能需要使用 SCAN 命令遍歷 product_name_index 并檢查成員是否包含關鍵詞
        Set<Tuple> nameIndexTuples = jedis.zrangeWithScores("product_name_index", 0, -1);
        for (Tuple tuple : nameIndexTuples) {
            String member = tuple.getElement();
            if (member.contains(nameKeyword)) {
                String productId = member.split(":")[1];
                nameMatchedProducts.add(productId);
            }
        }
        // 獲取價格范圍內的商品 ID 集合
        Set<String> priceMatchedProducts = jedis.zrangeByScore("product_price_index", minPrice, maxPrice);
        // 取交集
        priceMatchedProducts.retainAll(nameMatchedProducts);
        // 將交集轉換為有序集合(假設按價格排序)
        String tempIndexKey = "temp_index:" + UUID.randomUUID().toString();
        int score = 0;
        for (String productId : priceMatchedProducts) {
            jedis.zadd(tempIndexKey, jedis.zscore("product_price_index", productId), productId);
        }
        // 分頁查詢
        long start = (page - 1) * pageSize;
        long end = start + pageSize - 1;
        resultProductIds = jedis.zrange(tempIndexKey, start, end);
        // 刪除臨時索引
        jedis.del(tempIndexKey);
        return resultProductIds;
    }
    public static void main(String[] args) {
        RedisQueryDemo demo = new RedisQueryDemo();
        // 模擬寫入數據
        demo.addProduct("1001", "華為手機", 3000, "電子產品");
        demo.addProduct("1002", "小米手機", 2500, "電子產品");
        demo.addProduct("1003", "蘋果手機", 4000, "電子產品");
        demo.addProduct("1004", "華為平板", 2000, "電子產品");
        demo.addProduct("1005", "海爾冰箱", 3500, "家電");
        // 模擬查詢:名稱包含"手機",價格在 2000 - 4000 之間,第 1 頁,每頁 2 條
        List<String> productIds = demo.searchProducts("手機", 2000, 4000, 1, 2);
        for (String productId : productIds) {
            System.out.println("查詢到的商品 ID:" + productId);
            // 這里可以根據 productId 獲取具體的商品信息
        }
    }
}

五、注意事項和優化技巧

(一)索引維護

建立的索引會增加 Redis 的內存占用,所以要根據實際的查詢需求,合理選擇需要建立索引的條件,不要建立過多無用的索引。同時,在數據更新(比如刪除、修改)時,要及時更新對應的索引,保證索引的一致性。

(二)性能優化

  1. 對于大規模數據,使用 SCAN 命令代替 KEYS 命令進行鍵的掃描,避免全量掃描影響 Redis 性能。
  2. 在進行集合交集、并集等操作時,注意集合的大小,如果集合過大,操作可能會比較耗時,可以考慮在應用層進行部分過濾,減少 Redis 層的操作壓力。
  3. 可以對常用的查詢結果進行緩存,比如熱門的查詢條件和分頁結果,減少重復查詢的開銷。

(三)數據結構選擇

根據不同的業務場景選擇合適的數據結構,比如有序集合適合需要排序和范圍查詢的場景,集合適合需要去重和交集、并集操作的場景,字符串適合存儲單個對象的詳細信息。

六、總結

通過上面的方案,咱們基本上解決了 Redis 分頁和多條件模糊查詢的難題。利用有序集合、集合等數據結構建立索引,對數據進行預處理,結合分頁算法,能夠在 Redis 中實現高效的分頁和多條件查詢。當然,具體的實現還需要根據項目的實際需求進行調整和優化,比如索引的建立方式、數據結構的選擇、查詢條件的處理等。

責任編輯:武曉燕 來源: 石杉的架構筆記
相關推薦

2023-11-17 15:34:03

Redis數據庫

2025-02-06 10:00:52

RedisSpring高性能

2022-06-20 15:19:51

前端監控方案

2010-05-06 14:11:55

Oracle多條件查詢

2010-04-30 09:34:24

Oracle多條件查詢

2021-04-17 07:40:01

N卡驅動應用NVCleanstal

2009-09-15 09:33:46

linq多條件查詢

2018-06-11 17:27:56

APP流量華為

2010-04-28 16:45:27

Oracle Inst

2009-09-15 11:34:47

Linq多條件查詢

2023-07-12 08:01:28

FOADMROADMOXC

2010-11-09 15:18:37

SQL Server多

2009-06-29 09:03:31

Hibernate多條

2021-03-25 15:32:21

深度學習編程人工智能

2010-09-25 16:42:45

sql語句

2021-12-23 17:04:26

戴爾

2024-12-16 07:10:00

DockerDrone開發

2025-04-11 09:30:42

2022-09-14 08:11:06

分頁模糊查詢

2017-05-11 15:01:43

Androidweb布局
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线中文字幕国产 | 少妇一级淫片免费放播放 | 日韩亚洲一区二区 | 亚洲人免费视频 | 激情在线视频网站 | 羞羞在线观看视频 | 国产精品高潮呻吟久久av黑人 | 久久久久久久国产 | 欧美韩一区二区三区 | 亚洲国产成人在线视频 | 欧美综合网 | 国产99精品 | 免费av手机在线观看 | 亚洲a在线观看 | 久久精品国产一区二区三区不卡 | 久久精品| 国产精品伦一区二区三级视频 | 精品久久99| 国产视频在线一区二区 | 91精品国产综合久久国产大片 | 国产免费视频在线 | 久久尤物免费一区二区三区 | 毛片一级片 | 日韩欧美国产电影 | 国产欧美日韩精品在线观看 | 亚洲成人午夜电影 | 色视频在线免费观看 | 亚洲精品1区2区3区 91免费看片 | 亚洲精品久久久久久久久久久 | 青青草免费在线视频 | 欧美a级成人淫片免费看 | 欧美一区二区三区在线视频 | 亚洲国产成人精品久久 | av片在线观看网站 | 欧美成人一区二区 | 午夜激情视频 | 中文字幕在线剧情 | 日韩二区| 国产成人aⅴ | 国产欧美日韩精品在线观看 | 性福视频在线观看 |