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

Redis存儲總用String?你大概錯過了更優的使用方法

存儲 Redis
Redis為我們提供了5種數據類型,基本上我們使用頻率最高的就是String,而對其他四種數據類型使用的頻次稍弱于String。

 [[317695]]

Redis為我們提供了5種數據類型,基本上我們使用頻率最高的就是String,而對其他四種數據類型使用的頻次稍弱于String。原因在于:

  • String使用起來比較簡單,可以方便存儲復雜的對象,使用場景比較多;
  • 由于Redis expire time只能設置在key上,像List、Hash、Set、Zset屬于集合類型,會管理一組item,我們無法在這些集合的item上設置過期時間,所以使用expiretime來處理集合的cache失效會變得稍微復雜些。但是String使用expire time來管理過期策略會比較簡單,因為它包含的項少。這里說的集合是寬泛的類似集合。
  • 從更深層次來看,我們對另外四種數據類型的使用和原理并不是太了解。所以這個時候往往會忽視在特定場景下使用某種數據類型會比String性能高出很多的可能性,比如使用Hash結構來提高某實體某個項的修改等。

這里我們不打算羅列這5種數據類型的使用方法,因為這些資料網上有很多。我們主要討論這5種數據類型的功能特點,弄清楚它們分別適合用于處理哪些現實的業務場景,我們又該如何組合性使用這5種數據類型,找到解決復雜cache問題的最優方案。

一、Redis的數據類型及特點

我們來簡要了解一下String、List、Hash、Set及Zset:

1)String

String是Redis提供的字符串類型??梢葬槍tring類型獨立設置expire time,通常用來存儲長字符串數據,比如某個對象的json字符串。

在使用上,String類型最巧妙的是可以動態拼接key。通常我們可以將一組id放在Set里,然后動態查找String還是否存在,如果不存在說明已經過期或者由于數據修改主動delete了,需要再做一次cache數據load。

雖然Set無法設置item的過期時間,但是我們可以將Set Item與String Key關聯來達到相同的效果。

下圖中的左邊是一個key為Set:order:ids的Set集合,它可能是一個全量集合,也可能是某個查詢條件獲取出來的一個集合:

 

Redis存儲總用String?你大概錯過了更優的使用方法

 

有時候復雜點的場景需要多個Set集合來支撐計算,在Redis服務器里可能會有很多類似這樣的集合。這些集合我們可以稱為功能數據,這些數據是用來輔助cache計算的,當進行各種集合運算之后會得出當前查詢需要返回的子集,最后我們才會去獲取某個訂單真正的數據。

這些String:order:{orderId}字符串key并不一定是為了服務一種場景,而是整個系統最底層的數據,各種場景最后都需要獲取這些數據。那些Set集合可以認為是查詢條件數據,用來輔助查詢條件的計算。

Redis為我們提供了TYPE命令來查看某個key的數據類型,如String類型:

  1. SET string:order:100 order-100 
  2. TYPE string:order:100 
  3. string 

2)List

List在提高throughput的場景中非常適用,因為它特有的LPUSH、RPUSH、LPOP、RPOP功能可以無縫的支持生產者、消費者架構模式。

這非常適合實現類似Java Concurrency Fork/Join框架中的work-stealing算法(工作竊取)。

注:Java Fork/Join框架使用并行來提高性能,但是會帶來由于并發take task帶來的race condition(競態條件)問題,所以采用work-stealing算法來解決由于競爭問題帶來的性能損耗。

下圖中模擬了一個典型的支付callback峰值場景:

Redis存儲總用String?你大概錯過了更優的使用方法

在峰值出現的地方一般我們都會使用加buffer的方式來加快請求處理速度,這樣才能提高并發處理能力,提高through put。

支付gateway收到callback之后不做任何處理直接交給分發器。

分發器是一個無狀態的cluster,每個node通過向注冊中心pull handler queue list,也就是獲取下游處理器注冊到注冊中心里的消息通道。每一個分發器node會維護一個本地queue list,然后順序推送消息到這些queue list即可。

這里會有點小問題,就是支付gateway調用分發器的時候,是如何做load balance?如果不是平均負載可能會有某個queue list高出其他queue list。

而分發器不需要做soft load balance,因為哪怕某個queue list比其他queue list多也無所謂,因為下游message handler會根據work-stealing算法來竊取其他消費慢的queue list。

Redis List的LPUSH、RPUSH、LPOP、RPOP特性確實可以在很多場景下提高這種橫向擴展計算能力。

3)Hash

Hash數據類型很明顯是基于Hash算法的,對于項的查找時間復雜度是O(1)的,在極端情況下可能出現項Hash沖突問題,Redis內部是使用鏈表加key判斷來解決的。具體Redis內部的數據結構我們在后面有介紹,這里就不展開了。

Hash數據類型的特點通??梢杂脕斫鉀Q帶有映射關系,同時又需要對某些項進行更新或者刪除等操作。如果不是某個項需要維護,那么一般可以通過使用String來解決。

如果有需要對某個字段進行修改,使用String很明顯會多出很多開銷,需要讀取出來反序列化成對象然后操作,然后再序列化寫回Redis,這中間可能還有并發問題。

那我們可以使用Redis Hash提供的實體屬性Hash存儲特性,我們可以認為Hash Value是一個Hash Table,實體的每一個屬性都是通過Hash得到屬性的最終數據索引。

下圖使用Hash數據類型來記錄頁面的a/bmetrics:

Redis存儲總用String?你大概錯過了更優的使用方法

左邊的是首頁index的各個區域的統計,右邊是營銷marketing的各個區域統計。

在程序里我們可以很方便的使用Redis的atomic特性對Hash某個項進行累加操作。

  1. HMSET hash:mall:page:ab:metrics:index topbanner 10 leftbanner 5 rightbanner 8 bottombanner 20 productmore 10 topshopping 8 
  2. OK 
  3. HGETALL hash:mall:page:ab:metrics:index 
  4.  1) "topbanner" 
  5.  2) "10" 
  6.  3) "leftbanner" 
  7.  4) "5" 
  8.  5) "rightbanner" 
  9.  6) "8" 
  10.  7) "bottombanner" 
  11.  8) "20" 
  12.  9) "productmore" 
  13. 10) "10" 
  14. 11) "topshopping" 
  15. 12) "8" 
  16.   
  17. HINCRBY hash:mall:page:ab:metrics:index topbanner 1 
  18. (integer) 11 

使用Redis Hash Increment進行原子增加操作。HINCRBY命令可以原子增加任何給定的整數,也可以通過HINCRBYFLOAT來原子增加浮點類型數據。

4)Set

Set集合數據類型可以支持集合運算,不能存儲重復數據。

Set最大的特點就是集合的計算能力,inter交集、union并集、diff差集,這些特點可以用來做高性能的交叉計算或者剔除數據。

Set集合在使用場景上還是比較多和自由的。舉個簡單的例子,在應用系統中比較常見的就是商品、活動類場景。用一個Set緩存有效商品集合,再用一個Set緩存活動商品集合。如果商品出現上下架操作只需要維護有效商品Set,每次獲取活動商品的時候需要過濾下是否有下架商品,如果有就需要從活動商品中剔除。

當然,下架的時候可以直接刪除緩存的活動商品,但是活動是從marketing系統中load出來的,就算我將cache里的活動商品刪除,當下次再從marketing系統中load活動商品時候還是會有下架商品。

當然這只是舉例,一個場景有不同的實現方法。

下圖中左右兩邊是兩個不同的集合:

Redis存儲總用String?你大概錯過了更優的使用方法

左邊是營銷域中的可用商品ids集合,右邊是營銷域中活動商品ids集合,中間計算出兩個集合的交集。

  1. SADD set:marketing:product:available:ids 1000100 1000120 1000130 1000140 1000150 1000160 
  2.   
  3. SMEMBERS set:marketing:product:available:ids 
  4. 1) "1000100" 
  5. 2) "1000120" 
  6. 3) "1000130" 
  7. 4) "1000140" 
  8. 5) "1000150" 
  9. 6) "1000160" 
  10.   
  11. SADD set:marketing:activity:product:ids 1000100 1000120 1000130 1000140 1000200 1000300 
  12.   
  13. SMEMBERS set:marketing:activity:product:ids 
  14. 1) "1000100" 
  15. 2) "1000120" 
  16. 3) "1000130" 
  17. 4) "1000140" 
  18. 5) "1000200" 
  19. 6) "1000300" 
  20.   
  21. SINTER set:marketing:product:available:ids set:marketing:activity:product:ids 
  22. 1) "1000100" 
  23. 2) "1000120" 
  24. 3) "1000130" 
  25. 4) "1000140" 

在一些復雜的場景中,也可以使用SINTERSTORE命令將交集計算后的結果存儲在一個目標集合中。這在使用pipeline命令管道中特別有用,將SINTERSTORE命令包裹在pipeline命令串中可以重復使用計算出來的結果集。

由于Redis是Signle-Thread單線程模型,基于這個特性我們就可以使用Redis提供的pipeline管道來提交一連串帶有邏輯的命令集合,這些命令在處理期間不會被其他客戶端的命令干擾。

5)Zset

Zset排序集合與Set集合類似,但是Zset提供了排序的功能。在介紹Set集合的時候我們知道Set集合中的成員是無序的,Zset填補了集合可以排序的空隙。

Zset最強大的功能就是可以根據某個score比分值進行排序,這在很多業務場景中非常急需。比如,在促銷活動里根據商品的銷售數量來排序商品,在旅游景區里根據流入人數來排序熱門景點等?;旧先藗冊谧鋈魏问虑槎夹枰鶕承l件進行排序。

其實Zset在我們應用系統中能用到地方到處都是,這里我們舉一個簡單的例子,在團購系統中我們通常需要根據參團人數來排序成團列表,大家都希望參加那些即將成團的團。

下圖是一個根據團購code創建的Zset,score分值就是參團人數累加和:

Redis存儲總用String?你大概錯過了更優的使用方法
  1. ZADD zset:marketing:groupon:group:codes 5 G_PXYJY9QQFA 8 G_4EXMT6NZJQ 20 G_W7BMF5QC2P 10 G_429DHBTGZX 8 G_KHZGH9U4PP 
  2.   
  3. ZREVRANGEBYSCORE zset:marketing:groupon:group:codes 1000 0 
  4. 1) "G_W7BMF5QC2P" 
  5. 2) "G_ZMZ69HJUCB" 
  6. 3) "G_429DHBTGZX" 
  7. 4) "G_KHZGH9U4PP" 
  8. 5) "G_4EXMT6NZJQ" 
  9. 6) "G_PXYJY9QQFA" 
  10.   
  11. ZREVRANGEBYSCORE zset:marketing:groupon:group:codes 1000 0 withscores 
  12.  1) "G_W7BMF5QC2P" 
  13.  2) "20" 
  14.  3) "G_ZMZ69HJUCB" 
  15.  4) "10" 
  16.  5) "G_429DHBTGZX" 
  17.  6) "10" 
  18.  7) "G_KHZGH9U4PP" 
  19.  8) "8" 
  20.  9) "G_4EXMT6NZJQ" 
  21. 10) "8" 
  22. 11) "G_PXYJY9QQFA" 
  23. 12) "5" 

Zset本身提供了很多方法用來進行集合的排序,如果需要score分值,可以使用withscore字句帶出每一項的分值。

在一些比較特殊的場合可能需要組合排序,可能有多個Zset分別用來對同一個實體在不同維度的排序,按時間排序、按人數排序等。這個時候就可以組合使用Zset帶來的便捷性,利用pipeline再結合多個Zset最終得出組合排序集合。

二、案例:滬江團購系統大促hot-top接口cache設計

以滬江團購系統大促hot-top接口cache設計為例,我們總結了Redis提供的5種數據類型的各自特點和一般的使用場景。但是我們不僅僅可以分開使用這些數據類型,我們完全可以綜合使用這些數據類型來完成復雜的cache場景。

下面我們分享一個使用多個Zset、String來優化團購系統前臺接口的例子。由于篇幅和時間限制,這里只介紹跟本次案例相關的信息。

注:hot-top接口是指熱點、排名接口的意思,表示它的瀏覽量、并發量比較高,一般大促的時候都會有幾個這種性能要求比較高的接口。

我們先來分析一個查詢接口所包含的常規信息。

首先一個查詢接口肯定是有query condition查詢條件,然后是sort排序信息、最后是page分頁信息。這是一般接口所承擔的基本職責,當然,特殊場景下還需要支持master/slave replication時關于數據session一致性的要求,需要提供跟蹤標記來回master查詢數據,這里就不展開了。

我們可以抽象出這幾個維度的信息:

  • querycondition:查詢條件,companyid =100,sellerid=1010101諸如此類。
  • sort:排序信息,一般是默認一個列排序,但是在復雜的場景下會有可能讓接口使用者定制排序字段,比如一些租戶信息列。
  • page:分頁信息,簡單理解就是數據記錄排完序之后的第幾行到第幾行。

由于這里我們純粹用Redis來提高cache能力,不涉及到有關于任何搜索的能力,所以這里忽略其他復雜查詢的情況。其實我們在復雜的地方使用了Elastcsearch來提高搜索能力。

上述我們分析總結出了一個查詢接口的基本信息,這里還有一個有關于高并發接口的設計原則,就是將hot-top接口和一般search接口分離開,因為只有分而治之才能分別根據特點選用不同的技術。

如果我們不分職責將所有的查詢場景封裝在一個接口里,那么在后面優化接口性能的時候基本就很麻煩了,有些場景是無法或者很難用cache來解決的,因為接口里耦合了各種場景邏輯,就算勉強能實現性能也不會高。

前面做這些鋪墊是為了能在介紹案例的時候達成一個基本的共識?,F在我們來看下這個團購系統的hot-top接口的具體邏輯。

注:在大促的時候需要展現團購列表,這個接口的訪問量是非常大的,團購活動需要根據參團人數倒序排序,并且分頁返回指定數量的團列表。我們假設這個接口名為getTopGroups(getTopGroupsRequestrequest)。

1)query condition查詢條件問題

我們來仔細分析下,首先不同的查詢條件從DB里查詢出來的數據是不一樣的,也就是說查詢出來的團列表是不一樣的,可能有company公司、channel渠道等過濾條件。

由于一個團購活動下不會有太多團,頂多上百個是極限了,所以一個查詢條件出來的團列表也頂多幾十個,而且根據場景分析熱點查詢條件不會超過十個,所以我們選擇將查詢條件Hash出一個code來緩存本次查詢條件的全量團列表集合,但是這些結果集是沒有任何排序的。

2)sort排序問題

再看根據參團人數排序問題,我們立刻就可以想到使用Zset來處理團排序問題,因為只有一個排序維度,所以一個Zset就夠了。我們使用一個Zset來緩存所有團的參團人數集合,它是一個全量的團排序集合。

那么我們如何將用戶的查詢條件出來的團列表根據參團人數排序呢?剛好可以使用Zset的交集運算,直接計算出當前這個集合的Zset子集。

3)page分頁問題

通過對已經排序之后的團列表Zset使用Zrange來獲取出分頁集合。我們來看下完整的流程,如何處理查詢、排序、分頁的。

下圖從query condition計算Hash Code,然后通過DB查詢出當前條件全量團列表:

Redis存儲總用String?你大概錯過了更優的使用方法

zset:marketing:groupon:hottop:available:groupkey表示全量團的參團人數,用一個Zset來緩存。接著將這兩個Zset計算交集,就可以得出當前查詢所需要的帶有參團人數的Zset,最后在使用Zrevrange獲取分頁區間。

  1. ZADD zset:marketing:groupon:hottop:condition:2986080 0 G4ZD5732YZQ 0 G5VW3YF42UC 0 GF773FEJ7CC 0 GFW8DUEND8S 0 GKPKKW8XEY9 0 GL324DGWMZM 
  2. (integer) 6 
  3.   
  4. ZADD zset:marketing:groupon:hottop:available:group 5 GN7KQH36ZWK 10 GS7VB22AWD4 15 GF773FEJ7CC 17 G5VW3YF42UC 18 G4ZD5732YZQ 32 GTYJKCEJBRR 40 GKPKKW8XEY9 45 GL324DGWMZM 50 GFW8DUEND8S 60 GYTKY4ACWLT 
  5. (integer) 10 
  6.   
  7. ZINTERSTORE zset:marketing:groupon:hottop:condition:interstore 2 zset:marketing:groupon:hottop:condition:2986080 zset:marketing:groupon:hottop:available:group 
  8. (integer) 6 
  9.   
  10. ZRANGE zset:marketing:groupon:hottop:condition:interstore 0 -1 withscores 
  11.  1) "GF773FEJ7CC" 
  12.  2) "15" 
  13.  3) "G5VW3YF42UC" 
  14.  4) "17" 
  15.  5) "G4ZD5732YZQ" 
  16.  6) "18" 
  17.  7) "GKPKKW8XEY9" 
  18.  8) "40" 
  19.  9) "GL324DGWMZM" 
  20. 10) "45" 
  21. 11) "GFW8DUEND8S" 
  22. 12) "50" 
  23.   
  24. ZREVRANGE zset:marketing:groupon:hottop:condition:interstore 2 4 withscores 
  25. 1) "GKPKKW8XEY9" 
  26. 2) "40" 
  27. 3) "G4ZD5732YZQ" 
  28. 4) "18" 
  29. 5) "G5VW3YF42UC" 
  30. 6) "17" 

有了返回的團code集合之后就可以通過mget來批量獲取String類型的團詳情信息,這里就不貼出代碼了。

由于篇幅和時間關系,我們不展開太多的業務場景介紹了。這其中還涉及到計算cache過期時間的問題,這也跟促銷活動的運營規則有關系,還涉及到有可能query condition hash沖突問題等,但是這些已經不與我們本節主題相關。

 

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2019-01-08 13:45:39

Redis存儲數據庫

2019-12-27 15:18:01

微軟

2019-01-08 11:57:10

Redis存儲數據結構

2024-05-24 10:51:51

框架Java

2017-08-01 10:47:12

瓦戈

2023-04-11 16:31:10

開發React 庫Web

2018-11-27 05:46:10

等待時間悖論公交車數據分析

2022-09-19 00:46:18

JavaScrip功能開發

2011-02-24 13:09:10

FireFTP

2012-01-13 09:55:54

jQuery

2020-09-01 15:10:15

編程CSSJava

2018-02-04 14:44:11

2012-11-14 13:55:10

2016-03-04 11:06:20

更優秀程序員

2017-09-13 09:45:37

iPhone XiPhone 8

2009-12-24 16:36:06

WPF InkCanv

2010-10-08 16:01:17

mysql UPDAT

2013-07-15 15:12:40

iOS多線程NSOperationNSOperation
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 香蕉一区| 国产精品久久久亚洲 | 日韩一区二区免费视频 | 欧美一级黄色片在线观看 | 91精品久久久久久久久久小网站 | 亚洲欧美激情精品一区二区 | 日韩av一区二区在线观看 | 99久久精品免费视频 | 手机av网 | av色在线| 欧洲精品一区 | 成人夜晚看av | 国产a区 | 91人人爽 | 日韩 欧美 综合 | 精品国产一区二区三区久久久蜜月 | 黄色一级毛片免费看 | 欧美 日韩 国产 成人 在线 | 色婷婷久久久亚洲一区二区三区 | 一区二区三区免费 | 福利影院在线看 | 国产激情视频网站 | 久久亚洲国产精品日日av夜夜 | 亚洲精选久久 | 亚洲中午字幕 | 亚洲人a | 午夜网站视频 | 天天做日日做 | 欧美精品久久久久久久久久 | 91麻豆精品国产91久久久更新资源速度超快 | 国产人成精品一区二区三 | wwww.8888久久爱站网 | 亚洲欧洲av在线 | 日韩成人在线电影 | 狠狠操狠狠干 | av黄色在线| 欧美在线 | 亚洲视频一区二区三区四区 | 久久久久久91 | 国产高清在线精品一区二区三区 | 亚洲精品欧美一区二区三区 |