天天看點

Redis實作分頁+多條件模糊查詢組合方案

作者:大資料與人工智能分享

導言

Redis是一個高效的記憶體資料庫,它支援包括String、List、Set、SortedSet和Hash等資料類型的存儲,在Redis中通常根據資料的key查詢其value值,Redis沒有模糊條件查詢,在面對一些需要分頁、排序以及條件查詢的場景時(如評論,時間線,檢索等),隻憑借Redis所提供的功能就不太好不處理了。

本文不對Redis的特性做過多贅述。由于之前基于業務問題需要實作基于Redis的條件查詢和分頁功能,在百度上查詢了不少文章,基本不是隻有分頁功能就是隻有條件查詢功能的實作,缺少兩者組合的解決方案。是以,本文将基于Redis提供條件查詢+分頁的技術解決方案。

注:本文隻提供實作思路,并不提供實作的代碼

本文将從四個部分進行說明:

  • 分頁實作
  • 模糊條件查詢實作
  • 分頁和模糊條件查詢的組合實作
  • 優化方案

大家可以直接跳到自己需要的部分進行閱讀。

Redis的分頁實作

我們通常習慣于在Mysql、Oracle這樣持久化資料庫中實作分頁查詢,但是基于某些特殊的業務場景下,我們的資料并未持久化到了資料庫中或是出于查詢速度上的考慮将熱點資料加載到了緩存資料庫中。是以,我們可能需要基于Redis這樣的緩存資料庫去進行分頁查詢。

Redis的分頁查詢的實作是基于Redis提供的ZSet資料結構實作的,ZSet全稱為Sorted Set,該結構主要存儲有序集合。下面是它的指令描述以及該指令在分頁實作中的作用:

  • ZADD:SortedSet的添加元素指令ZADD key score member [[score,member]…]會給每個添加的元素member綁定一個用于排序的值score,SortedSet就會根據score值的大小對元素進行排序。我們為通常習慣于将資料的時間屬性當作score用于排序,當然大家也可以根據具體的業務場景去選擇排序的目标。
  • ZREVRANGE:SortedSet中的指令ZREVRANGE key start stop可以傳回指定區間内的成員,可以用來做分頁。
  • ZREM:SortedSet的指令ZREM key member可以根據key移除指定的成員,能滿足删評論的要求。

是以SortedSet用來做分頁是非常适合的。下面是分頁實作的示範圖,包含插入新記錄後的查詢情況。

Redis實作分頁+多條件模糊查詢組合方案

事實上,Redis中的List結構也是可以實作分頁,但List無法實作自動排序,并且Zset還可以根據score進行資料篩選,取出目标score區間内資料。

是以在實作上,ZSet往往更加适合我們。當然如果你需要插入重複資料的情況下,分頁就可能就需要借助List來實作了。具體使用那種結構來實作分頁還是需要根據實際的業務場景來進行選擇的。

Redis的多條件模糊查詢實作

Redis是key-value類型的記憶體資料庫,通過key直接取資料雖然很友善,但是并未提供像mysql那樣友善的sql條件查詢支援。是以我們需要借助Redis提供的結構和功能去自己實作模糊條件查詢功能。

事實上,Redis的模糊條件查詢是基于Hash實作的,我們可以将資料的某些條件值作為hash的key值,并資料本身作為value進行存儲。然後通過Hash提供的HSCAN指令去周遊所有的key進行篩選,得到我們符合條件的所有key值(hscan可以進行模式比對)。

為了友善,我們通常将符合條件的key全部放入到一個Set或是List中。這樣一來,我們就可以根據得到的key值去取出相應的資料了。下面是模糊查詢的示範圖(其中field中的命名規則為<id>:<姓名>:<性别>,value為使用者詳情的json串)。

查詢所有性别為女的使用者

Redis實作分頁+多條件模糊查詢組合方案

查詢所有名字中姓阿的使用者

Redis實作分頁+多條件模糊查詢組合方案

HSCAN雖然為我們提供了模式比對的功能,但這種比對是基于周遊實作的,每一次比對都需要周遊全部的key,效率上并不高。是以在下面一節會這方面進行補充,本節隻談如何實作模糊比對。

Redis的分頁+多條件模糊查詢組合實作

前面分别單獨叙述了如何實作Redis的分頁和多條件某查詢。在實際使用中,單獨使用ZSet實作分頁已經能夠展現不錯的性能了,但存在一個問題是我們所分頁的資料往往是伴随着一些動态的篩選條件的,而ZSet并不提供這樣的功能。

面對這種情況,我們通常有兩種解決方案:

  1. 如果資料已經存儲在了持久化資料庫中,我們可以每次在資料庫中做好條件查詢再将資料放入Redis中進行分頁。
  2. 在Redis中實作多條件模糊查詢并分頁。

前者方案其實是一個不錯的選擇,但缺點在于資料有時候并不一定都在持久化資料庫中。在有些業務場景下,我們的資料為了展現更好的并發性以及高響應,我們的資料會先放置在緩存資料庫中,等到某個時間或者滿足某種條件時再持久化到資料庫中。

在這種情況下我們第一個方案就不起作用了,需要使用第二個方案。是以,下面将介紹如何實作多條件模糊查詢的基礎上進行分頁。

實作思路

首先我們可以采用多條件模糊查詢章節所說的方式,将我們所涉及到的條件字段作為hash的field,而資料的内容則作為對應value進行存儲(一般以json格式存儲,友善反序列化)。

我們需要實作約定好查詢的格式,用前面一節的例子來說,field中的命名規則為<id>:<姓名>:<性别>,我們每次可以通過"*"來實作我們希望的模糊比對條件,比如“*:*:男”就是比對所有男性資料,“100*:*:*”就是比對所有id字首為100的使用者。

當我們拿到了比對串後我們先去Redis中尋找是否存在以該比對串為key的ZSet,如果沒有則通過Redis提供的HSCAN周遊所有hash的field,得到所有符合條件的field,并将其放入一個ZSet集合,同時将這個集合的key設定為我們的條件比對串。如果已經存在了,則直接對這個ZSet進行分頁查詢即可。對ZSet進行分頁的方式已經在前面叙述過了。通過這樣的方式我們就實作了最簡單的分頁+多條件模糊查詢。

Redis實作分頁+多條件模糊查詢組合方案

上圖中,由于并未在緩存資料庫中找到符合的ZSet集合,我們将根據比對串生成一個新的集合用于分頁。

性能優化方案

雖然上文實作了多條件模糊查詢+分頁的功能,但是在時間開發中,我們不能無限制的生成新的集合,因為比對串是很多樣化的,這會給緩存帶來巨大的壓力。

是以我們在生成集合時可以賦予這個集合一個過期時間,到期集合會自動銷毀。因為根據時間局部性原理,我們在一段時間内不通路的資料大機率在很長一頓時間内也不會再通路。而對于命中的集合,我們将更新其過期時間。

同時,我們資料的實時性也是一個問題,因為我們的集合是在生成集合時的Hash内容決定的,對于新插入到Hash的資料,集合是無法探知的,是以有兩種解決方案:

  • 第一種是插入到Hash時同時再插入到其他相應的集合中,保證資料一直是最新的,這種方式需要增加特殊字首用于識别,否則我們也不清楚到底要插入到哪些集合中。
  • 第二種方式是定時更新,這種方式比較省力,但無法保證分頁資料的實時性。是以具體怎麼選擇還是取決于業務場景。

總結

本文大概地描述了實作分頁和多條件模糊查詢的方案,希望能夠對大家有所幫助。

繼續閱讀