1. 【Redis】Redis中的小特性大作用

        共 5792字,需瀏覽 12分鐘

         ·

        2022-10-12 19:42

        Pipeline

        Pipeline介紹

        客戶端發(fā)送一條命令,Redis服務(wù)端執(zhí)行這條命令的整個過程如下:

        7e30b712d3b36ae363ea680a8d982696.webp


        如上圖所示,Redis客戶端執(zhí)行一條命令分為以下幾個部分:

        1. 1.?發(fā)送命令

        2. 2.?命令排隊

        3. 3.?執(zhí)行命令

        4. 4.?返回結(jié)果

        那么客戶端執(zhí)行一條命令的時間 = RTT(對應(yīng)1和4)+ 排隊時間 + 命令執(zhí)行時間;對于Redis這種內(nèi)存操作的系統(tǒng)來說,命令的執(zhí)行時間和排隊時間可能就是那么幾十微秒,而RTT時間和客戶端與服務(wù)端的距離有關(guān),如果客戶端和服務(wù)器處于不同城市,RTT時間可能高達(dá)10毫秒級別,可以看出,如果能夠?qū)⒍鄠€命令打包成一條請求執(zhí)行,相當(dāng)于原來需要多次RTT時間,現(xiàn)在只需要一次RTT時間,大大提升了性能。

        ??未使用pipeline執(zhí)行N條命令 執(zhí)行N條命令花費(fèi)時間:N*RTT + N *(命令執(zhí)行時 + 排隊時間)

        1c26ecb21c108e1944f526bb1f67be29.webp

        ??使用了pipeline執(zhí)行N條命令 執(zhí)行N條命令花費(fèi)時間:1*RTT + N *(命令執(zhí)行時 + 排隊時間

        • 8f7076ab9aa75e3251ce2bc53a16dd85.webp

        為了直觀上感覺pipeline效率的提高,我們可以測試一把,但是需要注意的是pipeline功能在命令行中沒有,但是各個語言版的client中都有相應(yīng)的實(shí)現(xiàn),這里我們使用java來使用pipeline功能。

            
              import?redis.clients.jedis.Jedis;
        import?redis.clients.jedis.Pipeline;

        public?class?PipelineDemo?{
        ????public?static?void?main(String[]?args)?{
        ????????Jedis?redis?=?new?Jedis("127.0.0.1",?6379);
        ????????redis.select(0);//使用第0個庫
        ????????redis.flushDB();//清空第0個庫所有數(shù)據(jù)

        ????????long?start?=?System.currentTimeMillis();
        ????????for?(int?i?=?0;?i?<?100000;?i++)?{
        ????????????redis.set("key_"?+?i,?i+?"");?//循環(huán)執(zhí)行10000條數(shù)據(jù)插入redis
        ????????}
        ????????long?end?=?System.currentTimeMillis();
        ????????System.out.println("正常插入"?+?redis.dbSize()?+?"數(shù)據(jù),?花費(fèi)時間:"?+?(end?-?start)?+?"?ms");


        ????????redis.flushDB();//清空第0個庫所有數(shù)據(jù)
        ????????long?startPipe?=?System.currentTimeMillis();
        ????????
        Pipeline?pipe?=?redis.pipelined();

        ????????for?(int?i?=?0;?i?<?100000;?i++)?{
        ????????????
        pipe.set ("key_"?+?i,?i+?"");?//將命令封裝到PIPE對象,此時并未執(zhí)行,還停留在客戶端
        ????????}
        ????????
        pipe.sync();? //將封裝后的PIPE一次性發(fā)給redis
        ????????long?endPipe?=?System.currentTimeMillis();
        ????????System.out.println("使用Pipleline插入"?+?redis.dbSize()?+?"數(shù)據(jù),?花費(fèi)時間:"?+?(endPipe-?startPipe)?+?"?ms");
        ????}
        }

        ***************執(zhí)行結(jié)果***************
        正常插入100000數(shù)據(jù),?花費(fèi)時間:4154?ms
        使用Pipleline插入100000數(shù)據(jù),?花費(fèi)時間:420?ms

        可以看出,同樣是設(shè)置10000個鍵,普通的set命令執(zhí)行10000次需要4154 ms, 而使用Pipleline只需要420ms,差不多提升了10倍的性能。

        使用場景

        Pipeline雖然好用,但也不是什么場景下都適合使用Pipeline技術(shù)。

        有些系統(tǒng)可能對可靠性要求很高,每次操作都需要立馬知道這次操作是否成功,是否數(shù)據(jù)已經(jīng)寫進(jìn)redis了,那這種場景就不適合

        而有的系統(tǒng),可能是批量的將數(shù)據(jù)寫入redis,允許一定比例的寫入失敗,那么這種場景就可以使用了,比如10000條一下進(jìn)入redis,可能失敗了2條無所謂,后期有補(bǔ)償機(jī)制就行了

        Pipeline雖然好用,但是每次Pipeline組裝的命令個數(shù)不能沒有節(jié)制,否則一次組裝Pipeline數(shù)據(jù)量過大,一方面會增加客戶端的等待時間,另一方面會造成一定的網(wǎng)絡(luò)阻塞,可以將一次包含大量命令的Pipeline拆分成多次較小的Pipeline來完成

        Bitmap

        Bitmap介紹

        Bitmap并不是一種新的數(shù)據(jù)結(jié)構(gòu),其本質(zhì)上就是字符串對應(yīng)的ASCII編碼組成的bit數(shù)組,比如"bi"對應(yīng)的ASCII碼分別是98、105,其在底層存儲為:

        b29f1e209e88d0bd19c2e1184a8c340e.webp

        因此Bitmap 看成是一個 bit 為單位的數(shù)組,數(shù)組的每個單元只能存儲 0 或者 1,數(shù)組的下標(biāo)在 Bitmap 中叫做 offset 偏移量。Redis中的Bitmap提供了位操作,極大地 節(jié)省存儲空間 。

        Bitmap操作

        Redis主要提供了4個Bitmap 的操作命令,如下:

            
              //設(shè)置key的第offset位(從0開始計數(shù))值
        SETBIT?<key>?<offset>?<value>

        //獲取 key 的 value 在 offset 處的 bit 位的值,當(dāng) key 不存在時,返回?0。
        GETBIT?<key>?<offset>

        //獲取Bitmap指定范圍值為1的個數(shù)
        BITCOUNT?[start]?[end]

        //OP取值:and(交集)or(并集)not(非)xor(異或)
        BITOP?OP?destkey?key[key?....]

        使用場景

        這里列出一個Bitmap常用的使用場景,根據(jù)前面介紹的命令進(jìn)行實(shí)操如下:

        判斷用戶登錄狀態(tài);

        某個網(wǎng)站有50個用戶,想要統(tǒng)計某天訪問的用戶數(shù)(UV,同一用戶一天訪問多次算作一次),假設(shè)2022-09-06這天,id為0, 3, 5的用戶登錄過網(wǎng)站,具體操作如下:

        也可以很方便獲取某個用戶有沒有在這一天登錄過網(wǎng)站:


        統(tǒng)計連續(xù)登錄的用戶

        接著上面的示例,2022-09-07這天有3, 5, 8三個用戶登錄,2022-09-08這天有3, 5, 9三個用戶的登錄,現(xiàn)在來統(tǒng)計6號到8號連續(xù)3天都登錄的用戶有多少:

        可以看出有2個用戶連續(xù)三天都登錄了。

        還有其它很多場景都可以用到Bitmap,比如ip 是否是黑名單、以及簽到打卡統(tǒng)計等場景。

        HyperLogLog

        HyperLogLog介紹

        要介紹HyperLogLog,首先需要了解一個概念:什么是基數(shù)?涉及到數(shù)學(xué)的概念一般都挺難理解的,這里還是舉個例子:有一個數(shù)據(jù)集 {1, 2, 5, 7, 2, 7, 8}, 那么這個數(shù)據(jù)集的基數(shù)集為 {1, 2, 5 ,7, 8}, 基數(shù)(不重復(fù)元素的個數(shù))為5。通過例子顯而易見,所謂基數(shù),就是在一批數(shù)據(jù)集中,不重復(fù)元素的個數(shù)。

        HyperLogLog 就是一種基數(shù)估算算法,既然是估算算法,那么肯定是允許有一定范圍的誤差。

        需要注意的是HyperLogLog并不是一種數(shù)據(jù)結(jié)構(gòu),而是一種算法,本文我們只介紹其使用場景和使用方法,具體實(shí)現(xiàn)我們不去細(xì)挖。

        Redis Hyperloglog 操作

        Redis提供了3個Hyperloglog 的操作命令
            
              //添加操作
        pfadd?key?elment1?elment2....

        //統(tǒng)計操作
        pfcount?key1?key2...

        //統(tǒng)計操作
        pfmerge??destkey?key1?key2...

        還是用統(tǒng)計UV的案例來演示這幾個命令。

        某個網(wǎng)站在2020年10月3號id為1、3、5、6、7、9的用戶訪問了,其中id為3和5的用戶訪問了2次(計算Uv只能算一次),使用Hyperloglog 命令如下: 7b1a5d237cbd8f4eb6e9afa22e5c79a6.webp2020年10月4號id為1、3、8的用戶訪問了該網(wǎng)站,現(xiàn)在需要計算3號和4號訪問網(wǎng)站的用戶(同一用戶不同天訪問也算一次訪問):

        e077a351ab0cbe0c54527a27acc2ff63.webp


        使用場景

        舉個栗子:假如我要統(tǒng)計網(wǎng)頁的UV(瀏覽用戶數(shù)量,一天內(nèi)同一個用戶多次訪問只能算一次),傳統(tǒng)的解決方案是使用set來保存用戶id,然后統(tǒng)計set中的元素數(shù)量來獲取頁面UV。但這種方案的缺點(diǎn)是一旦用戶數(shù)量大起來就需要消耗大量的空間來存儲用戶id。我們的目的是統(tǒng)計用戶數(shù)量而不是保存用戶,很顯然這是個吃力不討好的方案!

        而使用Redis的HyperLogLog最多需要12k內(nèi)存就可以滿足我們的需求,盡管其有大概0.81%的錯誤率,但是對于統(tǒng)計UV這種不需要很精確數(shù)據(jù)的業(yè)務(wù)場景來說是可以容忍的。

        GEO

        GEO介紹

        Redis 3.2 版本新增了geo相關(guān)命令,用于存儲和操作地理位置信息。提供的命令包括添加、計算位置之間距離、根據(jù)中心點(diǎn)坐標(biāo)和距離范圍來查詢地理位置集合等,

        Redis GEO 操作

            
              //添加member地理位置操作
        geoadd?key?longitude?latitude?member

        //獲取兩個地理位置的距離,[unit]表示單位:
        // m:?米 km:?千米 mi:英里? ft:尺
        geodist?key?member1?member2?...?[unit]

        //可以獲取地理位置的坐標(biāo),
        geopos??key?member1?member2?...

        //獲取元素的經(jīng)緯度編碼字符串
        geohash?key?member1?member2?...

        //根據(jù)坐標(biāo)點(diǎn)查找附近位置的元素
        //withcoord:返回結(jié)果中包含經(jīng)緯度?? withdist:返回結(jié)果中包含離指定點(diǎn)位置距離
        //withhash:返回結(jié)果中包含geohash COUNT count:指定返回結(jié)果的數(shù)量
        //asc|desc:按照離指定位置距排序方式
        //storedist?key:將返回結(jié)離指定位置距離保存到指定鍵key,該選項(xiàng)不能和withdist選項(xiàng)同時出現(xiàn)
        georadis?key?longitude?latitude?unit?[withcoord]?[withdist]?[withhash]?[COUNT?count]?[asc|desc]?[storedist?key]

        georadiusbymember?key?member?unit?[withcoord]?[withdist]?[withhash]?[COUNT?count]?[asc|desc]?[storedist?key]
        下面是我國五個城市的經(jīng)緯度表:

        針對這幾個地方,練習(xí)Redis的GEO 操作

        63fee83443dc5b0d55f9cac6d247cb63.webp


        使用場景

        我想大家應(yīng)該都使用軟件搜索過附近的人、附近的店等等,這種都離不開基于位置服務(wù)(Location-Based Service,LBS)的應(yīng)用。此類應(yīng)用都是基于經(jīng)緯度來查詢附近的目標(biāo),Redis GEO就適用于此類場景。

        0b5174fafa035ca1deba677cc54b5358.webp


        本文示例環(huán)境:
        Redis版本:4.0.10
        操作系統(tǒng):macOs 12.1
        JDK版本:12.0.1
        maven版本: 3.8.4

        —??—

        歡迎關(guān)注原創(chuàng)技術(shù)號↓ ↓↓ 原創(chuàng)不易,如有幫助,辛苦點(diǎn)贊和在看
        瀏覽 60
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 国产又粗又黄又猛又爽的视频 | 亚洲玖玖视频 | 骚丝淫逼| 秋霞丝鲁片一区二区三区手机在绒免 | 三级黄色片欧美 |