摘要:數據結構之應用之常用命令之應用場景說明本文參考了開發實戰指南,還有實戰自己之前的筆記。我們正式進入主題吧,中種數據結構的使用場景介紹。應用場景土法建索引。此命令會覆蓋哈希表中已存在的域。
數據結構之Redis應用之常用命令之應用場景 說明
1、本文參考了Redis開發實戰指南GitBook,還有《Redis實戰》自己之前的筆記。主體框架來自這里。
2、感謝大佬們的付出,在這里自己只是記錄,加深自己的印象。
3、本文會同步放到我自己的Guo_GitHub,方便自己復習,喜歡的可以點個star。
4、原諒我現在還沒能力自己總結,所以只能臨摹別人,加自己的理解,結合自己之前的筆記
5、如有拼寫錯誤,還請諒解。有不同觀點,可以評論出來,一起努力。
gogogo 我們正式進入主題吧,
Redis中5種數據結構的使用場景介紹Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs and geospatial indexes with radius queries. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster。
推介看《Redis In Action》非常非常非常不錯,尤其前三章。使用Redis的關鍵點不是設計數據庫,而是如何選擇合適場景的數據結構。就像我們之前操作容器時,選擇不同容易裝不同對象。每個都有各自的各點,具體的內容過兩天看源碼的時候分享給大家。今天的主題是以下五種數據結構。
主要5種數據結構:
String ——>字符串
Hash ——>字典(哈希)
List ——>列表
Set ——>集合
Sorted Set ——>有序集合
1. String——字符串String數據結構是簡單的Key-Value類型,Value不僅可以是String,也可以是數字。使用String類型,可以完全實現目前Memcached(只有一種String)的功能,并且效率更高,還可以享受Redis的定時持久化(RDB模式或AOF模式),提供日志及Replication等功能。除了提供與Memcached的get、set、incr、decr等操作外,Redis還提供了下面一些操作:
LEN niushuai:O(1)獲取字符串長度
APPEND niushuai redis:往字符串 append 內容,而且采用智能分配內存(每次2倍)
設置和獲取字符串的某一段內容
設置及獲取字符串的某一位(bit)
批量設置一系列字符串的內容
原子計數器
GETSET 命令的妙用,請于清空舊值的同時設置一個新值,配合原子計數器使用
//1、計數器的使用 String articleId = String.valueOf(conn.incr("article:")); //String.valueOf(int i) : 將 int 變量 i 轉換成字符串
放一些常用命令,方便自己復習
set key value [ex 秒數] / [px 毫秒數] [nx] /[xx] //返回1表示成功,0失敗 incr key //對key做加加操作, decr key //對key做減減操作 setnx key value //僅當key不存在時才set,nx表示not exist。(重點) mset key1 value1 key2 value2 .。。 //一次設置多個key, -------------------------------------------------------------------------------- get key //如果key不存在返回null mget key1 key2 ... keyN //一次獲取多個key的值,如果對于的key不存在,則返回null getset key value //設置新值返回舊值。
分布式鎖的思路:
C3發送SETNX lock.foo 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個0
C3發送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。
反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
GETSET lock.foo
通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如愿以償拿到鎖了。
如果在C3之前,有個叫C4的客戶端比C3快一步執行了上面的操作,那么C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。
偽代碼:
# get lock lock = 0 while lock != 1: timestamp = current Unix time + lock timeout + 1 //時間戳 lock = SETNX lock.foo timestamp //嘗試獲取鎖,返回0,則下面檢查是否超時,GET。 if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)): break; //關鍵點上面這條代碼 else: sleep(10ms) # do your job do_job() # release if now() < GET lock.foo: DEL lock.foo
gogogo
incr key //對key做加加操作, decr key //對key做減減操作 incrby key interge //對key加指定的數 incrbyfloat key floatnumber //針對浮點數 append key value //返回新字符的長度 substr key start end //并不會修改字符串,返回截取的字符串 getrange key start end //返回子串 strlen key //取指定key的Value2. Hash——字典
在Memcached中,經常就愛那個一些結構化的信息打包成hashMap,在客戶端序列化后存儲為一個字符串的值,(通常為JSON格式),比如用戶的昵稱、年齡、性別、積分。這時候在需要修改其中某一項時,通用需要將字符串(JSON)取出來,然后進行反序列化,修改某一項的值,在序列化成字符串(JSON)存儲回去。而Redis和Hash結構可以使你像在數據庫中Update一個屬性一樣只修改某一項屬性值。
底層實現是hash table,一般操作復雜度是O(1),要同時操作多個field時就是O(N),N是field的數量。應用場景:土法建索引。比如User對象,除了id有時還要按name來查詢。
常用命令:
hset key field value //設置hash field為指定值,如果key不存在,則先創建 hsetnx //同時,如果存在返回0,nx是not exist的意思 hmset key filed1 value1 ... filedN valueN //設置多個值 hget key field //獲取指定的hash field hmget key field1 field2 //獲取全部指定的field ------------------------------------------------------------------------------- hincrby key field integer //將指定的hash field加上給定值 hexists key field //測試指定field是否存在 hdel key field //刪除指定的field hlen key //返回會指定hash的field數量 hgetall //返回hash所有field和value
場景:
/** * 使用Redis重新實現登錄cookie,取代目前由關系型數據庫實現的登錄cookie功能 * 1、將使用一個散列來存儲登錄cookie令牌與與登錄用戶之間的映射。 * 2、需要根據給定的令牌來查找與之對應的用戶,并在已經登錄的情況下,返回該用戶id。 */ //1、嘗試獲取并返回令牌對應的用戶 注意“login:” 一般使用冒號做分割符,這是不成文的規矩 conn.hget("login:", token); //2、維持令牌與已登錄用戶之間的映射。 conn.hset("login:", token, user); //6、移除被刪除令牌對應的用戶信息 conn.hdel("login:", tokens); ---------------------------注意中間還有其他步驟---------------------------------- /** * 使用cookie實現購物車——就是將整個購物車都存儲到cookie里面, * 優點:無需對數據庫進行寫入就可以實現購物車功能, * 缺點:怎是程序需要重新解析和驗證cookie,確保cookie的格式正確。并且包含商品可以正常購買 * 還有一缺點:因為瀏覽器每次發送請求都會連cookie一起發送,所以如果購物車的體積較大, * 那么請求發送和處理的速度可能降低。 * ----------------------------------------------------------------- * 1、每個用戶的購物車都是一個散列,存儲了商品ID與商品訂單數量之間的映射。 * 2、如果用戶訂購某件商品的數量大于0,那么程序會將這件商品的ID以及用戶訂購該商品的數量添加到散列里。 * 3、如果用戶購買的商品已經存在于散列里面,那么新的訂單數量會覆蓋已有的。 * 4、相反,如果某用戶訂購某件商品數量不大于0,那么程序將從散列里移除該條目 * 5、需要對之前的會話清理函數進行更新,讓它在清理會話的同時,將舊會話對應的用戶購物車也一并刪除。 */ //1、從購物車里面移除指定的商品 注意"cart:" 可以在數據遷移、轉換 、和刪除時輕松識別 conn.hdel("cart:" + session, item); //2、將指定的商品添加到購物車 conn.hset("cart:" + session, item, String.valueOf(count)); //6、移除被刪除令牌對應的用戶信息 conn.hdel("login:", tokens); -------------------------------------------------------------------------------- //5、將文章信息存儲到一個散列里面。 //HMSET key field value [field value ...] //同時將多個 field-value (域-值)對設置到哈希表 key 中。 //此命令會覆蓋哈希表中已存在的域。 conn.hmset(article, articleData); //為哈希表 key 中的域 field 的值加上增量 increment 。 //增量也可以為負數,相當于對給定域進行減法操作。 //HINCRBY counter page_view 200 conn.hincrBy(article, "votes", 1L); //如果返回1表示還沒有這個數據,注意-1后面的L conn.hincrBy(article, "votes", -1L); //3、根據文章ID獲取文章的詳細信息 MaparticleData = conn.hgetAll(id); --------------------------測試------------------------------------------------- //2、測試文章的投票過程 articleVote(conn, "other_user", "article:" + articleId); String votes = conn.hget("article:" + articleId, "votes"); System.out.println("我們為該文章投票,目前該文章的票數 " + votes); assert Integer.parseInt(votes) > 1;
其實應用場景還有很多。這里只是摘出程序片段中的一部分,具體可以點這里。需要注意的是組合使用,取其精華、去其糟粕。
3. List——列表List說白了就是鏈表(Redis使用雙端鏈表實現的List),使用List結構,我們可以輕松的實現最新消息的排行等功能(比如Twitter的TimeLine),List的另一個應用就是消息隊列,可以利用List的PUSH操作,將任務存在List中,然后工作線POP操作取出任務執行。Redis還提供了操作List中某一段元素的API,可以直接查詢,刪除List中某一段元素。
命令:
lpush key string //在key對應的list的頭部添加元素 rpush key string //在list的尾部添加元素 lpushx key value //如果key不存在,什么都不做 rpushx key value //同上 linsert key BEFORE|AFTER pivot value //在list對應的位置之前或之后 --------------------------------------------------------------------------- llen key //查看列表對應的長度 lindex key index //指定索引的位置,0第一個 lrange key start end //查看一段列表 lrange key 0 -1 // -1表示返回所有數據 ltrim key start end //保留指定區間的元素 lset key index value //idnex表示指定索引的位置 ldel //刪除元素 blpop key [key ...] timeout //阻塞隊列 brpop key [key ...] timeout
基于redis構建消息隊列點這里,寫的非常不錯。為了自己復習,就拿來主義了。
一般來說,消息隊列有兩種場景:一種是發布者訂閱者模式;一種是生產者消費者模式。利用redis這兩種場景的消息隊列都能夠實現。
生產者消費者模式:生產者生產消息放到隊列里,多個消費者同時監聽隊列,誰先搶到消息誰就會從隊列中取走消息;即對于每個消息只能被最多一個消費者擁有。(常用于處理高并發寫操作)
發布者訂閱者模式:發布者生產消息放到隊列里,多個監聽隊列的消費者都會收到同一份消息;即正常情況下每個消費者收到的消息應該都是一樣的。(常用來作為日志收集中一份原始數據對多個應用場景)
1、redis作為消息中間件:
1)Producer/ConsumerMode:
該方式是借助redis的list結構實現的。Producer調用redis的lpush往特定key里塞入消息,Consumer調用brpop(阻塞方法)去不斷監聽該key。
// producer code String key = "demo:mq:test"; String msg = "hello world"; redisDao.lpush(key, msg); // consumer code String key = "demo:mq:test"; while (true) { // block invoke Listmsgs = redisDao.brpop(BLOCK_TIMEOUT, listKey); //注意brpop if (msgs == null) continue; String jobMsg = msgs.get(1); processMsg(jobMsg); }
1、使用redis怎么做消息隊列
1、首先redis它的設計是用來做緩存的,但是由于它自身的某種特性使得他可以用來做消息隊列。它有幾個阻塞式的API(brpop、Sub,他們都是阻塞版的)可以使用,正是這些阻塞式的API讓他有做消息隊列的能力。
2、其次,消息隊列的其他特性例如FIFO也很容易實現,只需要一個List對象從頭取數據,從尾部塞數據即可實現。
2、訂閱-發布系統
Pub/Sub 從字面上理解就是發布(Publish)與訂閱(Subscribe),在 Redis 中,你可以設定對某一個 key 值進行消息發布及消息訂閱,當一個 key 值上進行了消息發布后,所有訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用作實時消息系統,比如普通的即時聊天,群聊等功能。
代碼實現點這里和上面同一個人,已征求:
要使用Jedis的Publish/Subscribe功能,必須編寫對JedisPubSub的自己的實現。
public class MyListener extends JedisPubSub { // 取得訂閱的消息后的處理 @Override public void onMessage(String channel, String message) { // TODO Auto-generated method stub System.out.println(channel + "=" + message); } // 取得按表達式的方式訂閱的消息后的處理 @Override public void onPMessage(String pattern, String channel, String message) { // TODO Auto-generated method stub System.out.println(pattern + ":" + channel + "=" + message); } // 初始化訂閱時候的處理 @Override public void onSubscribe(String channel, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("初始化 【頻道訂閱】 時候的處理 "); } // 取消訂閱時候的處理 @Override public void onUnsubscribe(String channel, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("http:// 取消 【頻道訂閱】 時候的處理 "); } // 初始化按表達式的方式訂閱時候的處理 @Override public void onPSubscribe(String pattern, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("初始化 【模式訂閱】 時候的處理 "); } // 取消按表達式的方式訂閱時候的處理 @Override public void onPUnsubscribe(String pattern, int subscribedChannels) { // TODO Auto-generated method stub System.out.println("取消 【模式訂閱】 時候的處理 "); } }
2、Sub
public class Sub { public static void main(String[] args) { try { Jedis jedis = getJedis(); MyListener ml = new MyListener(); //可以訂閱多個頻道 //jedis.subscribe(ml, "liuxiao","hello","hello_liuxiao","hello_redis"); //jedis.subscribe(ml, new String[]{"hello_foo","hello_test"}); //這里啟動了訂閱監聽,線程將在這里被阻塞 //訂閱得到信息在lister的onPMessage(...)方法中進行處理 //使用模式匹配的方式設置頻道 jedis.psubscribe(ml, new String[]{"hello_*"}); } catch (Exception e) { e.printStackTrace(); } } }
需要注意的是:
調用subscribe()或psubscribe() 時,當前線程都會阻塞。
3、Pub
public class Pub { public static void main(String[] args) { try { Jedis jedis = getJedis(); jedis.publish("hello_redis","hello_redis"); } catch (Exception e) { e.printStackTrace(); } } }4. Set——集合(重點)
Set 就是一個集合,集合的概念就是一堆不重復值的組合。利用 Redis 提供的 Set 數據結構,可以存儲一些集合性的數據。比如在Twitter應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。因為 Redis 非常人性化的為集合提供了求交集、并集、差集等操作,那么就可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。
共同好友、二度好友
利用唯一性,可以統計訪問網站的所有獨立 IP
好友推薦的時候,根據 tag 求交集,大于某個 threshold 就可以推薦
命令:
sadd key member //添加元素,成功返回1, srem key member //移除元素,成功返回1 spop key //刪除并返回,如果set是空或者不存在則返回null srandmember key //同spop,隨機取set中一個元素,但是不刪除 smove srckey dstkey member //集合間移動元素 scard key //查看集合的大小,如果set是空或者key不存在則返回0 sismember key member //判斷member是否在Set中,存在返回1,0表示不存在或key不存在 smembers key //獲取所有元素,返回key對應的所有元素,結果是無序的哦 -------------------------------------------------------------------------------- //集合交集 sinter key1 key2 //返回所有給定key的交集 sinterstore dstkey key1 key2 //同sinter,并同時保存并集到dstkey下 //集合并集 sunion key1 key2 //返回所有給定key的并集 sunionstore dstkey key1 key2 //同sunion,并同時保存并集到dstkey下 //集合差集 sdiff key1 key2 //返回給定key的差集 sdiffstore dstkey key1 key2 //同sdiff,并同時保存并集到dstkey下
為了防止用戶對同一篇文章進行多次投票,需要為每篇文章記錄一個已投票用戶名單。使用集合來存儲已投票的用戶ID。由于集合是不能存儲多個相同的元素的,所以不會出現同個用戶對同一篇文章多次投票的情況。
代碼實現:
2、程序需要使用SADD將文章發布者的ID添加到記錄文章已投票用戶名單的集合中 并使用EXPIRE命令為這個集合設置一個過期時間,讓Redis在文章發布期滿一周之后自動刪除這個集合。 //2、添加到記錄文章已投用戶名單中, conn.sadd(voted, user); //3、設置一周為過期時間 conn.expire(voted, ONE_WEEK_IN_SECONDS); -------------------------------------------------------------------------------- 4、檢查用戶是否第一次為這篇文章投票,如果是第一次,則在增加這篇文章的投票數量和評分。 將一個或多個 member 元素加入到集合 key 當中,已經存在于集合的 member 元素將被忽略。 if (conn.sadd("voted:" + articleId, user) == 1) { //ZINCRBY salary 2000 tom # tom 加薪啦! conn.zincrby("score:", VOTE_SCORE, article); //HINCRBY counter page_view 200 conn.hincrBy(article, "votes", 1L); } --------------------------------------------------------------------------------- /** * 記錄文章屬于哪個群組 * 將所屬一個群組的文章ID記錄到那個集合中 * Redis不僅可以對多個集合執行操作,甚至在一些情況下,還可以在集合和有序集合之間執行操作 */ //1、構建存儲文章信息的鍵名 String article = "article:" + articleId; for (String group : toAdd) { //2、將文章添加到它所屬的群組里面 conn.sadd("group:" + group, article); }5. Sorted Set——有序集合(重點之重點)
Sorted Set的實現是hash table(element->score, 用于實現ZScore及判斷element是否在集合內),和skip list(score->element,按score排序)的混合體。 skip list有點像平衡二叉樹那樣,不同范圍的score被分成一層一層,每層是一個按score排序的鏈表。 ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是結果/操作元素的個數。可見,原本可能很大的N被很關鍵的Log了一下,1000萬大小的Set,復雜度也只是幾十不到。當然,如果一次命中很多元素M很大那誰也沒辦法了。
常用命令 :
zadd key score member //添加元素到集合,元素在集合中存在則更新對應的score zrem key member //1表示成功,如果元素不存在則返回0 zremrangebyrank min max //刪除集合中排名在給定的區間 zincrvy key member //增加對于member的score的值。 zcard key //返回集合中元素的個數 //獲取排名 zrank key member //返回指定元素在集合中的排名, zrebrank key member //同時,但是集合中的元素是按score從大到小排序的 //獲取排行榜 zrange key start end //類似lrange操作從集合中去指定區間的元素,返回時有序的。 //給給定分數區間的元素 zrangebyscore key min max //評分的聚合 ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
代碼實現:
/** * 1、每次用戶瀏覽頁面的時候,程序需都會對用戶存儲在登錄散列里面的信息進行更新, * 2、并將用戶的令牌和當前時間戳添加到記錄最近登錄用戶的集合里。 * 3、如果用戶正在瀏覽的是一個商品,程序還會將商品添加到記錄這個用戶最近瀏覽過的商品有序集合里面, * 4、如果記錄商品的數量超過25個時,對這個有序集合進行修剪。 */ //1、獲取當前時間戳 long timestamp = System.currentTimeMillis() / 1000; //2、維持令牌與已登錄用戶之間的映射。 conn.hset("login:", token, user); //3、記錄令牌最后一次出現的時間 conn.zadd("recent:", timestamp, token); if (item != null) { //4、記錄用戶瀏覽過的商品 conn.zadd("viewed:" + token, timestamp, item); //5、移除舊記錄,只保留用戶最近瀏覽過的25個商品 conn.zremrangeByRank("viewed:" + token, 0, -26); //6、為有序集key的成員member的score值加上增量increment。通過傳遞一個負數值increment 讓 score 減去相應的值, conn.zincrby("viewed:", -1, item); } --------------------------------------------------------------------------------- /** *存儲會話數據所需的內存會隨著時間的推移而不斷增加,所有我們需要定期清理舊的會話數據。 * 1、清理會話的程序由一個循環構成,這個循環每次執行的時候,都會檢查存儲在最近登錄令牌的有序集合的大小。 * 2、如果有序集合的大小超過了限制,那么程序會從有序集合中移除最多100個最舊的令牌, * 3、并從記錄用戶登錄信息的散列里移除被刪除令牌對應的用戶信息, * 4、并對存儲了這些用戶最近瀏覽商品記錄的有序集合中進行清理。 * 5、于此相反,如果令牌的數量沒有超過限制,那么程序會先休眠一秒,之后在重新進行檢查。 */ public void run() { while (!quit) { //1、找出目前已有令牌的數量。 long size = conn.zcard("recent:"); //2、令牌數量未超過限制,休眠1秒,并在之后重新檢查 if (size <= limit) { try { sleep(1000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } continue; } long endIndex = Math.min(size - limit, 100); //3、獲取需要移除的令牌ID SettokenSet = conn.zrange("recent:", 0, endIndex - 1); String[] tokens = tokenSet.toArray(new String[tokenSet.size()]); ArrayList sessionKeys = new ArrayList (); for (String token : tokens) { //4、為那些將要被刪除的令牌構建鍵名 sessionKeys.add("viewed:" + token); } //5、移除最舊的令牌 conn.del(sessionKeys.toArray(new String[sessionKeys.size()])); //6、移除被刪除令牌對應的用戶信息 conn.hdel("login:", tokens); //7、移除用戶最近瀏覽商品記錄。 conn.zrem("recent:", tokens); } } } //7、移除用戶最近瀏覽商品記錄。 conn.zrem("recent:", tokens);
另外一個案例:
/** * 為了應對促銷活動帶來的大量負載,需要對數據行進行緩存,具體做法是: * 1、編寫一個持續運行的守護進程,讓這個函數指定的數據行緩存到redis里面,并不定期的更新。 * 2、緩存函數會將數據行編碼為JSON字典并存儲在Redis字典里。其中數據列的名字會被映射為JSON的字典, * 而數據行的值則被映射為JSON字典的值。 * ----------------------------------------------------------------------------------------- * 程序使用兩個有序集合來記錄應該在何時對緩存進行更新: * 1、第一個為調用有序集合,他的成員為數據行的ID,而分支則是一個時間戳, * 這個時間戳記錄了應該在何時將指定的數據行緩存到Redis里面 * 2、第二個有序集合為延時有序集合,他的成員也是數據行的ID, * 而分值則記錄了指定數據行的緩存需要每隔多少秒更新一次。 * ---------------------------------------------------------------------------------------- * 為了讓緩存函數定期的緩存數據行,程序首先需要將hangID和給定的延遲值添加到延遲有序集合里面, * 然后再將行ID和當前指定的時間戳添加到調度有序集合里面。 */ public void scheduleRowCache(Jedis conn, String rowId, int delay) { //1、先設置數據行的延遲值 conn.zadd("delay:", delay, rowId); //2、立即對需要行村的數據進行調度 conn.zadd("schedule:", System.currentTimeMillis() / 1000, rowId); } -------------------------------------------------------------------------------------- /** * 1、通過組合使用調度函數和持續運行緩存函數,實現類一種重讀進行調度的自動緩存機制, * 并且可以隨心所欲的控制數據行緩存的更新頻率: * 2、如果數據行記錄的是特價促銷商品的剩余數量,并且參與促銷活動的用戶特別多的話,那么最好每隔幾秒更新一次數據行緩存: * 另一方面,如果數據并不經常改變,或者商品缺貨是可以接受的,那么可以每隔幾分鐘更新一次緩存。 */ public class CacheRowsThread extends Thread { private Jedis conn; private boolean quit; public CacheRowsThread() { this.conn = new Jedis("localhost"); this.conn.select(14); } public void quit() { quit = true; } public void run() { Gson gson = new Gson(); while (!quit) { //1、嘗試獲取下一個需要被緩存的數據行以及該行的調度時間戳,返回一個包含0個或一個元組列表 Setrange = conn.zrangeWithScores("schedule:", 0, 0); Tuple next = range.size() > 0 ? range.iterator().next() : null; long now = System.currentTimeMillis() / 1000; //2、暫時沒有行需要被緩存,休眠50毫秒。 if (next == null || next.getScore() > now) { try { sleep(50); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } continue; } //3、提前獲取下一次調度的延遲時間, String rowId = next.getElement(); double delay = conn.zscore("delay:", rowId); if (delay <= 0) { //4、不必在緩存這個行,將它從緩存中移除 conn.zrem("delay:", rowId); conn.zrem("schedule:", rowId); conn.del("inv:" + rowId); continue; } //5、繼續讀取數據行 Inventory row = Inventory.get(rowId); //6、更新調度時間,并設置緩存值。 conn.zadd("schedule:", now + delay, rowId); conn.set("inv:" + rowId, gson.toJson(row)); } } }
具體的內容看我之前寫的筆記購物網站的redis相關實現(Java)和文章投票網站的redis相關實現(Java).
這個Redis主題就算記錄完了,gogogo。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68705.html
摘要:如問到是否使用某框架,實際是是問該框架的使用場景,有什么特點,和同類可框架對比一系列的問題。這兩個方向的區分點在于工作方向的側重點不同。 [TOC] 這是一份來自嗶哩嗶哩的Java面試Java面試 32個核心必考點完全解析(完) 課程預習 1.1 課程內容分為三個模塊 基礎模塊: 技術崗位與面試 計算機基礎 JVM原理 多線程 設計模式 數據結構與算法 應用模塊: 常用工具集 ...
摘要:時間年月日星期日說明本文部分內容均來自慕課網。當對應的鏈表存在時,從左側插入數據。從右側插入數據。當系統在定時持久化之前出現宕機,還未來得及往硬盤寫入數據,那數據就丟失了。當數據集過大時,可能會導致服務器停止幾百毫秒甚至是秒鐘。 時間:2017年05月21日星期日說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:無個人學習源碼:https:...
閱讀 1388·2021-11-24 09:38
閱讀 2089·2021-09-22 15:17
閱讀 2385·2021-09-04 16:41
閱讀 3477·2019-08-30 15:56
閱讀 3516·2019-08-29 17:19
閱讀 1975·2019-08-28 18:09
閱讀 1253·2019-08-26 13:35
閱讀 1715·2019-08-23 17:52