亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

數(shù)據(jù)結(jié)構(gòu)之Redis應(yīng)用~常用命令~應(yīng)用場(chǎng)景(重點(diǎn))(一)

mcterry / 1191人閱讀

摘要:數(shù)據(jù)結(jié)構(gòu)之應(yīng)用之常用命令之應(yīng)用場(chǎng)景說(shuō)明本文參考了開(kāi)發(fā)實(shí)戰(zhàn)指南,還有實(shí)戰(zhàn)自己之前的筆記。我們正式進(jìn)入主題吧,中種數(shù)據(jù)結(jié)構(gòu)的使用場(chǎng)景介紹。應(yīng)用場(chǎng)景土法建索引。此命令會(huì)覆蓋哈希表中已存在的域。

數(shù)據(jù)結(jié)構(gòu)之Redis應(yīng)用之常用命令之應(yīng)用場(chǎng)景 說(shuō)明

1、本文參考了Redis開(kāi)發(fā)實(shí)戰(zhàn)指南GitBook,還有《Redis實(shí)戰(zhàn)》自己之前的筆記。主體框架來(lái)自這里。

2、感謝大佬們的付出,在這里自己只是記錄,加深自己的印象。

3、本文會(huì)同步放到我自己的Guo_GitHub,方便自己復(fù)習(xí),喜歡的可以點(diǎn)個(gè)star。

4、原諒我現(xiàn)在還沒(méi)能力自己總結(jié),所以只能臨摹別人,加自己的理解,結(jié)合自己之前的筆記

5、如有拼寫(xiě)錯(cuò)誤,還請(qǐng)諒解。有不同觀點(diǎn),可以評(píng)論出來(lái),一起努力。

gogogo 我們正式進(jìn)入主題吧,

Redis中5種數(shù)據(jù)結(jié)構(gòu)的使用場(chǎng)景介紹

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》非常非常非常不錯(cuò),尤其前三章。使用Redis的關(guān)鍵點(diǎn)不是設(shè)計(jì)數(shù)據(jù)庫(kù),而是如何選擇合適場(chǎng)景的數(shù)據(jù)結(jié)構(gòu)。就像我們之前操作容器時(shí),選擇不同容易裝不同對(duì)象。每個(gè)都有各自的各點(diǎn),具體的內(nèi)容過(guò)兩天看源碼的時(shí)候分享給大家。今天的主題是以下五種數(shù)據(jù)結(jié)構(gòu)。

主要5種數(shù)據(jù)結(jié)構(gòu):

String ——>字符串

Hash ——>字典(哈希)

List ——>列表

Set ——>集合

Sorted Set ——>有序集合

1. String——字符串

String數(shù)據(jù)結(jié)構(gòu)是簡(jiǎn)單的Key-Value類(lèi)型,Value不僅可以是String,也可以是數(shù)字。使用String類(lèi)型,可以完全實(shí)現(xiàn)目前Memcached(只有一種String)的功能,并且效率更高,還可以享受Redis的定時(shí)持久化(RDB模式或AOF模式),提供日志及Replication等功能。除了提供與Memcached的get、set、incr、decr等操作外,Redis還提供了下面一些操作:

LEN niushuai:O(1)獲取字符串長(zhǎng)度

APPEND niushuai redis:往字符串 append 內(nèi)容,而且采用智能分配內(nèi)存(每次2倍)

設(shè)置和獲取字符串的某一段內(nèi)容

設(shè)置及獲取字符串的某一位(bit)

批量設(shè)置一系列字符串的內(nèi)容

原子計(jì)數(shù)器

GETSET 命令的妙用,請(qǐng)于清空舊值的同時(shí)設(shè)置一個(gè)新值,配合原子計(jì)數(shù)器使用

//1、計(jì)數(shù)器的使用
    String articleId = String.valueOf(conn.incr("article:"));     //String.valueOf(int i) : 將 int 變量 i 轉(zhuǎn)換成字符串

放一些常用命令,方便自己復(fù)習(xí)

set key value [ex 秒數(shù)] / [px 毫秒數(shù)]  [nx] /[xx]     //返回1表示成功,0失敗
incr key                                             //對(duì)key做加加操作,
decr key                                             //對(duì)key做減減操作
setnx key value                                      //僅當(dāng)key不存在時(shí)才set,nx表示not exist。(重點(diǎn))
mset key1 value1   key2 value2 .。。                 //一次設(shè)置多個(gè)key,
--------------------------------------------------------------------------------
get key                                              //如果key不存在返回null
mget key1 key2 ... keyN                              //一次獲取多個(gè)key的值,如果對(duì)于的key不存在,則返回null
getset key value                                     //設(shè)置新值返回舊值。

分布式鎖的思路:

C3發(fā)送SETNX lock.foo 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個(gè)0

C3發(fā)送GET lock.foo 以檢查鎖是否超時(shí)了,如果沒(méi)超時(shí),則等待或重試。

反之,如果已超時(shí),C3通過(guò)下面的操作來(lái)嘗試獲得鎖:

GETSET lock.foo

通過(guò)GETSET,C3拿到的時(shí)間戳如果仍然是超時(shí)的,那就說(shuō)明,C3如愿以?xún)斈玫芥i了。

如果在C3之前,有個(gè)叫C4的客戶(hù)端比C3快一步執(zhí)行了上面的操作,那么C3拿到的時(shí)間戳是個(gè)未超時(shí)的值,這時(shí),C3沒(méi)有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒(méi)拿到鎖,但它改寫(xiě)了C4設(shè)置的鎖的超時(shí)值,不過(guò)這一點(diǎn)非常微小的誤差帶來(lái)的影響可以忽略不計(jì)。

偽代碼:

# get lock
lock = 0
while lock != 1:
    timestamp = current Unix time + lock timeout + 1       //時(shí)間戳
    lock = SETNX lock.foo timestamp                        //嘗試獲取鎖,返回0,則下面檢查是否超時(shí),GET。
    if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)):
        break;                                            //關(guān)鍵點(diǎn)上面這條代碼
    else:
        sleep(10ms)

# do your job
do_job()

# release
if now() < GET lock.foo:
    DEL lock.foo

gogogo

incr key                       //對(duì)key做加加操作,
decr key                       //對(duì)key做減減操作

incrby key interge             //對(duì)key加指定的數(shù)

incrbyfloat key floatnumber     //針對(duì)浮點(diǎn)數(shù)

append key value               //返回新字符的長(zhǎng)度

substr key start end           //并不會(huì)修改字符串,返回截取的字符串

getrange key start end         //返回子串

strlen  key                     //取指定key的Value
2. Hash——字典

在Memcached中,經(jīng)常就愛(ài)那個(gè)一些結(jié)構(gòu)化的信息打包成hashMap,在客戶(hù)端序列化后存儲(chǔ)為一個(gè)字符串的值,(通常為JSON格式),比如用戶(hù)的昵稱(chēng)、年齡、性別、積分。這時(shí)候在需要修改其中某一項(xiàng)時(shí),通用需要將字符串(JSON)取出來(lái),然后進(jìn)行反序列化,修改某一項(xiàng)的值,在序列化成字符串(JSON)存儲(chǔ)回去。而Redis和Hash結(jié)構(gòu)可以使你像在數(shù)據(jù)庫(kù)中Update一個(gè)屬性一樣只修改某一項(xiàng)屬性值。

底層實(shí)現(xiàn)是hash table,一般操作復(fù)雜度是O(1),要同時(shí)操作多個(gè)field時(shí)就是O(N),N是field的數(shù)量。應(yīng)用場(chǎng)景:土法建索引。比如User對(duì)象,除了id有時(shí)還要按name來(lái)查詢(xún)。

常用命令:

hset key field value                        //設(shè)置hash field為指定值,如果key不存在,則先創(chuàng)建
hsetnx                                      //同時(shí),如果存在返回0,nx是not exist的意思
hmset key filed1 value1 ... filedN valueN   //設(shè)置多個(gè)值

hget key field                              //獲取指定的hash field
hmget key field1 field2                     //獲取全部指定的field
-------------------------------------------------------------------------------
hincrby key field integer                  //將指定的hash field加上給定值
hexists key field                          //測(cè)試指定field是否存在
hdel  key field                            //刪除指定的field
hlen key                                   //返回會(huì)指定hash的field數(shù)量

hgetall                                    //返回hash所有field和value

場(chǎng)景:

/**
 * 使用Redis重新實(shí)現(xiàn)登錄cookie,取代目前由關(guān)系型數(shù)據(jù)庫(kù)實(shí)現(xiàn)的登錄cookie功能
 * 1、將使用一個(gè)散列來(lái)存儲(chǔ)登錄cookie令牌與與登錄用戶(hù)之間的映射。
 * 2、需要根據(jù)給定的令牌來(lái)查找與之對(duì)應(yīng)的用戶(hù),并在已經(jīng)登錄的情況下,返回該用戶(hù)id。
 */

//1、嘗試獲取并返回令牌對(duì)應(yīng)的用戶(hù)    注意“l(fā)ogin:” 一般使用冒號(hào)做分割符,這是不成文的規(guī)矩
 conn.hget("login:", token);
//2、維持令牌與已登錄用戶(hù)之間的映射。
conn.hset("login:", token, user);
//6、移除被刪除令牌對(duì)應(yīng)的用戶(hù)信息
conn.hdel("login:", tokens);
---------------------------注意中間還有其他步驟----------------------------------
/**
 * 使用cookie實(shí)現(xiàn)購(gòu)物車(chē)——就是將整個(gè)購(gòu)物車(chē)都存儲(chǔ)到cookie里面,
 * 優(yōu)點(diǎn):無(wú)需對(duì)數(shù)據(jù)庫(kù)進(jìn)行寫(xiě)入就可以實(shí)現(xiàn)購(gòu)物車(chē)功能,
 * 缺點(diǎn):怎是程序需要重新解析和驗(yàn)證cookie,確保cookie的格式正確。并且包含商品可以正常購(gòu)買(mǎi)
 * 還有一缺點(diǎn):因?yàn)闉g覽器每次發(fā)送請(qǐng)求都會(huì)連cookie一起發(fā)送,所以如果購(gòu)物車(chē)的體積較大,
 * 那么請(qǐng)求發(fā)送和處理的速度可能降低。
 * -----------------------------------------------------------------
 * 1、每個(gè)用戶(hù)的購(gòu)物車(chē)都是一個(gè)散列,存儲(chǔ)了商品ID與商品訂單數(shù)量之間的映射。
 * 2、如果用戶(hù)訂購(gòu)某件商品的數(shù)量大于0,那么程序會(huì)將這件商品的ID以及用戶(hù)訂購(gòu)該商品的數(shù)量添加到散列里。
 * 3、如果用戶(hù)購(gòu)買(mǎi)的商品已經(jīng)存在于散列里面,那么新的訂單數(shù)量會(huì)覆蓋已有的。
 * 4、相反,如果某用戶(hù)訂購(gòu)某件商品數(shù)量不大于0,那么程序?qū)纳⒘欣镆瞥摋l目
 * 5、需要對(duì)之前的會(huì)話清理函數(shù)進(jìn)行更新,讓它在清理會(huì)話的同時(shí),將舊會(huì)話對(duì)應(yīng)的用戶(hù)購(gòu)物車(chē)也一并刪除。
 */

//1、從購(gòu)物車(chē)?yán)锩嬉瞥付ǖ纳唐?       注意"cart:" 可以在數(shù)據(jù)遷移、轉(zhuǎn)換 、和刪除時(shí)輕松識(shí)別
conn.hdel("cart:" + session, item);
//2、將指定的商品添加到購(gòu)物車(chē)
conn.hset("cart:" + session, item, String.valueOf(count));
//6、移除被刪除令牌對(duì)應(yīng)的用戶(hù)信息
conn.hdel("login:", tokens);

--------------------------------------------------------------------------------
//5、將文章信息存儲(chǔ)到一個(gè)散列里面。
//HMSET key field value [field value ...]
//同時(shí)將多個(gè) field-value (域-值)對(duì)設(shè)置到哈希表 key 中。
//此命令會(huì)覆蓋哈希表中已存在的域。
conn.hmset(article, articleData);

//為哈希表 key 中的域 field 的值加上增量 increment 。
//增量也可以為負(fù)數(shù),相當(dāng)于對(duì)給定域進(jìn)行減法操作。
//HINCRBY counter page_view 200
conn.hincrBy(article, "votes", 1L);

//如果返回1表示還沒(méi)有這個(gè)數(shù)據(jù),注意-1后面的L
conn.hincrBy(article, "votes", -1L);
//3、根據(jù)文章ID獲取文章的詳細(xì)信息
Map articleData = conn.hgetAll(id);
--------------------------測(cè)試-------------------------------------------------
//2、測(cè)試文章的投票過(guò)程
articleVote(conn, "other_user", "article:" + articleId);
String votes = conn.hget("article:" + articleId, "votes");
System.out.println("我們?yōu)樵撐恼峦镀保壳霸撐恼碌钠睌?shù) " + votes);
assert Integer.parseInt(votes) > 1;

其實(shí)應(yīng)用場(chǎng)景還有很多。這里只是摘出程序片段中的一部分,具體可以點(diǎn)這里。需要注意的是組合使用,取其精華、去其糟粕。

3. List——列表

List說(shuō)白了就是鏈表(Redis使用雙端鏈表實(shí)現(xiàn)的List),使用List結(jié)構(gòu),我們可以輕松的實(shí)現(xiàn)最新消息的排行等功能(比如Twitter的TimeLine),List的另一個(gè)應(yīng)用就是消息隊(duì)列,可以利用List的PUSH操作,將任務(wù)存在List中,然后工作線POP操作取出任務(wù)執(zhí)行。Redis還提供了操作List中某一段元素的API,可以直接查詢(xún),刪除List中某一段元素。

命令:

lpush key string                        //在key對(duì)應(yīng)的list的頭部添加元素
rpush key string                        //在list的尾部添加元素

lpushx key value                        //如果key不存在,什么都不做
rpushx key value                        //同上

linsert key BEFORE|AFTER pivot value     //在list對(duì)應(yīng)的位置之前或之后
---------------------------------------------------------------------------
llen key                                 //查看列表對(duì)應(yīng)的長(zhǎng)度
lindex key index                         //指定索引的位置,0第一個(gè)

lrange key start end                     //查看一段列表
lrange key 0 -1                          // -1表示返回所有數(shù)據(jù)

ltrim key start end                      //保留指定區(qū)間的元素

lset key index value                     //idnex表示指定索引的位置

ldel                                     //刪除元素

blpop key [key ...] timeout               //阻塞隊(duì)列
brpop key [key ...] timeout

基于redis構(gòu)建消息隊(duì)列點(diǎn)這里,寫(xiě)的非常不錯(cuò)。為了自己復(fù)習(xí),就拿來(lái)主義了。

一般來(lái)說(shuō),消息隊(duì)列有兩種場(chǎng)景:一種是發(fā)布者訂閱者模式;一種是生產(chǎn)者消費(fèi)者模式。利用redis這兩種場(chǎng)景的消息隊(duì)列都能夠?qū)崿F(xiàn)。

生產(chǎn)者消費(fèi)者模式:生產(chǎn)者生產(chǎn)消息放到隊(duì)列里,多個(gè)消費(fèi)者同時(shí)監(jiān)聽(tīng)隊(duì)列,誰(shuí)先搶到消息誰(shuí)就會(huì)從隊(duì)列中取走消息;即對(duì)于每個(gè)消息只能被最多一個(gè)消費(fèi)者擁有。(常用于處理高并發(fā)寫(xiě)操作)

發(fā)布者訂閱者模式:發(fā)布者生產(chǎn)消息放到隊(duì)列里,多個(gè)監(jiān)聽(tīng)隊(duì)列的消費(fèi)者都會(huì)收到同一份消息;即正常情況下每個(gè)消費(fèi)者收到的消息應(yīng)該都是一樣的。(常用來(lái)作為日志收集中一份原始數(shù)據(jù)對(duì)多個(gè)應(yīng)用場(chǎng)景)

1、redis作為消息中間件:

1)Producer/ConsumerMode:

該方式是借助redis的list結(jié)構(gòu)實(shí)現(xiàn)的。Producer調(diào)用redis的lpush往特定key里塞入消息,Consumer調(diào)用brpop(阻塞方法)去不斷監(jiān)聽(tīng)該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
   List msgs = redisDao.brpop(BLOCK_TIMEOUT, listKey);    //注意brpop
       if (msgs == null) continue;
           String jobMsg = msgs.get(1);
               processMsg(jobMsg);
}

1、使用redis怎么做消息隊(duì)列

1、首先redis它的設(shè)計(jì)是用來(lái)做緩存的,但是由于它自身的某種特性使得他可以用來(lái)做消息隊(duì)列。它有幾個(gè)阻塞式的API(brpop、Sub,他們都是阻塞版的)可以使用,正是這些阻塞式的API讓他有做消息隊(duì)列的能力。

2、其次,消息隊(duì)列的其他特性例如FIFO也很容易實(shí)現(xiàn),只需要一個(gè)List對(duì)象從頭取數(shù)據(jù),從尾部塞數(shù)據(jù)即可實(shí)現(xiàn)。

2、訂閱-發(fā)布系統(tǒng)

Pub/Sub 從字面上理解就是發(fā)布(Publish)與訂閱(Subscribe),在 Redis 中,你可以設(shè)定對(duì)某一個(gè) key 值進(jìn)行消息發(fā)布及消息訂閱,當(dāng)一個(gè) key 值上進(jìn)行了消息發(fā)布后,所有訂閱它的客戶(hù)端都會(huì)收到相應(yīng)的消息。這一功能最明顯的用法就是用作實(shí)時(shí)消息系統(tǒng),比如普通的即時(shí)聊天,群聊等功能。

代碼實(shí)現(xiàn)點(diǎn)這里和上面同一個(gè)人,已征求:

要使用Jedis的Publish/Subscribe功能,必須編寫(xiě)對(duì)JedisPubSub的自己的實(shí)現(xiàn)。

public class MyListener extends JedisPubSub {

    // 取得訂閱的消息后的處理
    @Override
    public void onMessage(String channel, String message) {
        // TODO Auto-generated method stub
        System.out.println(channel + "=" + message);
    }

    // 取得按表達(dá)式的方式訂閱的消息后的處理
    @Override
    public void onPMessage(String pattern, String channel, String message) {
        // TODO Auto-generated method stub
        System.out.println(pattern + ":" + channel + "=" + message);
    }
    // 初始化訂閱時(shí)候的處理
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        // TODO Auto-generated method stub
        System.out.println("初始化 【頻道訂閱】 時(shí)候的處理  ");
    }
    // 取消訂閱時(shí)候的處理
    @Override
    public void onUnsubscribe(String channel, int subscribedChannels) {
        // TODO Auto-generated method stub
        System.out.println("http:// 取消 【頻道訂閱】 時(shí)候的處理  ");
    }

    // 初始化按表達(dá)式的方式訂閱時(shí)候的處理
    @Override
    public void onPSubscribe(String pattern, int subscribedChannels) {
        // TODO Auto-generated method stub
        System.out.println("初始化 【模式訂閱】 時(shí)候的處理  ");
    }
    // 取消按表達(dá)式的方式訂閱時(shí)候的處理
    @Override
    public void onPUnsubscribe(String pattern, int subscribedChannels) {
        // TODO Auto-generated method stub
        System.out.println("取消 【模式訂閱】 時(shí)候的處理  ");
    }
}

2、Sub

public class Sub {
    public static void main(String[] args) {
        try {
            Jedis jedis = getJedis();
            MyListener ml = new MyListener();

            //可以訂閱多個(gè)頻道
            //jedis.subscribe(ml, "liuxiao","hello","hello_liuxiao","hello_redis");
            //jedis.subscribe(ml, new String[]{"hello_foo","hello_test"});
            //這里啟動(dòng)了訂閱監(jiān)聽(tīng),線程將在這里被阻塞
            //訂閱得到信息在lister的onPMessage(...)方法中進(jìn)行處理

            //使用模式匹配的方式設(shè)置頻道
            jedis.psubscribe(ml, new String[]{"hello_*"});
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

需要注意的是:

調(diào)用subscribe()或psubscribe() 時(shí),當(dāng)前線程都會(huì)阻塞。

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——集合(重點(diǎn))

Set 就是一個(gè)集合,集合的概念就是一堆不重復(fù)值的組合。利用 Redis 提供的 Set 數(shù)據(jù)結(jié)構(gòu),可以存儲(chǔ)一些集合性的數(shù)據(jù)。比如在Twitter應(yīng)用中,可以將一個(gè)用戶(hù)所有的關(guān)注人存在一個(gè)集合中,將其所有粉絲存在一個(gè)集合。因?yàn)?Redis 非常人性化的為集合提供了求交集、并集、差集等操作,那么就可以非常方便的實(shí)現(xiàn)如共同關(guān)注、共同喜好、二度好友等功能,對(duì)上面的所有集合操作,你還可以使用不同的命令選擇將結(jié)果返回給客戶(hù)端還是存集到一個(gè)新的集合中。

共同好友、二度好友

利用唯一性,可以統(tǒng)計(jì)訪問(wèn)網(wǎng)站的所有獨(dú)立 IP

好友推薦的時(shí)候,根據(jù) tag 求交集,大于某個(gè) threshold 就可以推薦

命令:

sadd key member               //添加元素,成功返回1,

srem key member               //移除元素,成功返回1

spop key                     //刪除并返回,如果set是空或者不存在則返回null

srandmember key              //同spop,隨機(jī)取set中一個(gè)元素,但是不刪除

smove srckey dstkey member   //集合間移動(dòng)元素

scard key                    //查看集合的大小,如果set是空或者key不存在則返回0

sismember key member         //判斷member是否在Set中,存在返回1,0表示不存在或key不存在

smembers  key                 //獲取所有元素,返回key對(duì)應(yīng)的所有元素,結(jié)果是無(wú)序的哦

--------------------------------------------------------------------------------
//集合交集
sinter key1 key2                  //返回所有給定key的交集
sinterstore dstkey key1 key2      //同sinter,并同時(shí)保存并集到dstkey下

//集合并集
sunion key1 key2                 //返回所有給定key的并集
sunionstore dstkey key1 key2      //同sunion,并同時(shí)保存并集到dstkey下

//集合差集
sdiff key1 key2                   //返回給定key的差集
sdiffstore dstkey key1 key2       //同sdiff,并同時(shí)保存并集到dstkey下

為了防止用戶(hù)對(duì)同一篇文章進(jìn)行多次投票,需要為每篇文章記錄一個(gè)已投票用戶(hù)名單。使用集合來(lái)存儲(chǔ)已投票的用戶(hù)ID。由于集合是不能存儲(chǔ)多個(gè)相同的元素的,所以不會(huì)出現(xiàn)同個(gè)用戶(hù)對(duì)同一篇文章多次投票的情況。
代碼實(shí)現(xiàn):

2、程序需要使用SADD將文章發(fā)布者的ID添加到記錄文章已投票用戶(hù)名單的集合中
并使用EXPIRE命令為這個(gè)集合設(shè)置一個(gè)過(guò)期時(shí)間,讓Redis在文章發(fā)布期滿(mǎn)一周之后自動(dòng)刪除這個(gè)集合。
//2、添加到記錄文章已投用戶(hù)名單中,
conn.sadd(voted, user);
//3、設(shè)置一周為過(guò)期時(shí)間
conn.expire(voted, ONE_WEEK_IN_SECONDS);
--------------------------------------------------------------------------------
4、檢查用戶(hù)是否第一次為這篇文章投票,如果是第一次,則在增加這篇文章的投票數(shù)量和評(píng)分。
將一個(gè)或多個(gè) member 元素加入到集合 key 當(dāng)中,已經(jīng)存在于集合的 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);
}
---------------------------------------------------------------------------------
/**
 * 記錄文章屬于哪個(gè)群組
 * 將所屬一個(gè)群組的文章ID記錄到那個(gè)集合中
 * Redis不僅可以對(duì)多個(gè)集合執(zhí)行操作,甚至在一些情況下,還可以在集合和有序集合之間執(zhí)行操作
 */

 //1、構(gòu)建存儲(chǔ)文章信息的鍵名
String article = "article:" + articleId;
for (String group : toAdd) {
    //2、將文章添加到它所屬的群組里面
    conn.sadd("group:" + group, article);
}
5. Sorted Set——有序集合(重點(diǎn)之重點(diǎn))

Sorted Set的實(shí)現(xiàn)是hash table(element->score, 用于實(shí)現(xiàn)ZScore及判斷element是否在集合內(nèi)),和skip list(score->element,按score排序)的混合體。 skip list有點(diǎn)像平衡二叉樹(shù)那樣,不同范圍的score被分成一層一層,每層是一個(gè)按score排序的鏈表。 ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是結(jié)果/操作元素的個(gè)數(shù)??梢?jiàn),原本可能很大的N被很關(guān)鍵的Log了一下,1000萬(wàn)大小的Set,復(fù)雜度也只是幾十不到。當(dāng)然,如果一次命中很多元素M很大那誰(shuí)也沒(méi)辦法了。

常用命令 :

zadd key score member             //添加元素到集合,元素在集合中存在則更新對(duì)應(yīng)的score
zrem key member                   //1表示成功,如果元素不存在則返回0

zremrangebyrank min max           //刪除集合中排名在給定的區(qū)間

zincrvy key member                //增加對(duì)于member的score的值。

zcard key                         //返回集合中元素的個(gè)數(shù)

//獲取排名
zrank key member                 //返回指定元素在集合中的排名,
zrebrank key member              //同時(shí),但是集合中的元素是按score從大到小排序的

//獲取排行榜
zrange key start end            //類(lèi)似lrange操作從集合中去指定區(qū)間的元素,返回時(shí)有序的。

//給給定分?jǐn)?shù)區(qū)間的元素
zrangebyscore key min max

//評(píng)分的聚合
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

代碼實(shí)現(xiàn):

/**
 * 1、每次用戶(hù)瀏覽頁(yè)面的時(shí)候,程序需都會(huì)對(duì)用戶(hù)存儲(chǔ)在登錄散列里面的信息進(jìn)行更新,
 * 2、并將用戶(hù)的令牌和當(dāng)前時(shí)間戳添加到記錄最近登錄用戶(hù)的集合里。
 * 3、如果用戶(hù)正在瀏覽的是一個(gè)商品,程序還會(huì)將商品添加到記錄這個(gè)用戶(hù)最近瀏覽過(guò)的商品有序集合里面,
 * 4、如果記錄商品的數(shù)量超過(guò)25個(gè)時(shí),對(duì)這個(gè)有序集合進(jìn)行修剪。
 */

 //1、獲取當(dāng)前時(shí)間戳
 long timestamp = System.currentTimeMillis() / 1000;
 //2、維持令牌與已登錄用戶(hù)之間的映射。
 conn.hset("login:", token, user);
 //3、記錄令牌最后一次出現(xiàn)的時(shí)間
 conn.zadd("recent:", timestamp, token);
 if (item != null) {
     //4、記錄用戶(hù)瀏覽過(guò)的商品
     conn.zadd("viewed:" + token, timestamp, item);
     //5、移除舊記錄,只保留用戶(hù)最近瀏覽過(guò)的25個(gè)商品
     conn.zremrangeByRank("viewed:" + token, 0, -26);
     //6、為有序集key的成員member的score值加上增量increment。通過(guò)傳遞一個(gè)負(fù)數(shù)值increment 讓 score 減去相應(yīng)的值,
     conn.zincrby("viewed:", -1, item);
 }
---------------------------------------------------------------------------------
/**
 *存儲(chǔ)會(huì)話數(shù)據(jù)所需的內(nèi)存會(huì)隨著時(shí)間的推移而不斷增加,所有我們需要定期清理舊的會(huì)話數(shù)據(jù)。
 * 1、清理會(huì)話的程序由一個(gè)循環(huán)構(gòu)成,這個(gè)循環(huán)每次執(zhí)行的時(shí)候,都會(huì)檢查存儲(chǔ)在最近登錄令牌的有序集合的大小。
 * 2、如果有序集合的大小超過(guò)了限制,那么程序會(huì)從有序集合中移除最多100個(gè)最舊的令牌,
 * 3、并從記錄用戶(hù)登錄信息的散列里移除被刪除令牌對(duì)應(yīng)的用戶(hù)信息,
 * 4、并對(duì)存儲(chǔ)了這些用戶(hù)最近瀏覽商品記錄的有序集合中進(jìn)行清理。
 * 5、于此相反,如果令牌的數(shù)量沒(méi)有超過(guò)限制,那么程序會(huì)先休眠一秒,之后在重新進(jìn)行檢查。
 */

 public void run() {
         while (!quit) {
             //1、找出目前已有令牌的數(shù)量。
             long size = conn.zcard("recent:");
             //2、令牌數(shù)量未超過(guò)限制,休眠1秒,并在之后重新檢查
             if (size <= limit) {
                 try {
                     sleep(1000);
                 } catch (InterruptedException ie) {
                     Thread.currentThread().interrupt();
                 }
                 continue;
             }

             long endIndex = Math.min(size - limit, 100);
             //3、獲取需要移除的令牌ID
             Set tokenSet = conn.zrange("recent:", 0, endIndex - 1);
             String[] tokens = tokenSet.toArray(new String[tokenSet.size()]);

             ArrayList sessionKeys = new ArrayList();
             for (String token : tokens) {
                 //4、為那些將要被刪除的令牌構(gòu)建鍵名
                 sessionKeys.add("viewed:" + token);
             }
             //5、移除最舊的令牌
             conn.del(sessionKeys.toArray(new String[sessionKeys.size()]));
             //6、移除被刪除令牌對(duì)應(yīng)的用戶(hù)信息
             conn.hdel("login:", tokens);
             //7、移除用戶(hù)最近瀏覽商品記錄。
             conn.zrem("recent:", tokens);
         }
     }
}

//7、移除用戶(hù)最近瀏覽商品記錄。
conn.zrem("recent:", tokens);

另外一個(gè)案例:

/**
 * 為了應(yīng)對(duì)促銷(xiāo)活動(dòng)帶來(lái)的大量負(fù)載,需要對(duì)數(shù)據(jù)行進(jìn)行緩存,具體做法是:
 * 1、編寫(xiě)一個(gè)持續(xù)運(yùn)行的守護(hù)進(jìn)程,讓這個(gè)函數(shù)指定的數(shù)據(jù)行緩存到redis里面,并不定期的更新。
 * 2、緩存函數(shù)會(huì)將數(shù)據(jù)行編碼為JSON字典并存儲(chǔ)在Redis字典里。其中數(shù)據(jù)列的名字會(huì)被映射為JSON的字典,
 * 而數(shù)據(jù)行的值則被映射為JSON字典的值。
 * -----------------------------------------------------------------------------------------
 * 程序使用兩個(gè)有序集合來(lái)記錄應(yīng)該在何時(shí)對(duì)緩存進(jìn)行更新:
 * 1、第一個(gè)為調(diào)用有序集合,他的成員為數(shù)據(jù)行的ID,而分支則是一個(gè)時(shí)間戳,
 * 這個(gè)時(shí)間戳記錄了應(yīng)該在何時(shí)將指定的數(shù)據(jù)行緩存到Redis里面
 * 2、第二個(gè)有序集合為延時(shí)有序集合,他的成員也是數(shù)據(jù)行的ID,
 * 而分值則記錄了指定數(shù)據(jù)行的緩存需要每隔多少秒更新一次。
 * ----------------------------------------------------------------------------------------
 * 為了讓緩存函數(shù)定期的緩存數(shù)據(jù)行,程序首先需要將hangID和給定的延遲值添加到延遲有序集合里面,
 * 然后再將行ID和當(dāng)前指定的時(shí)間戳添加到調(diào)度有序集合里面。
 */
public void scheduleRowCache(Jedis conn, String rowId, int delay) {
    //1、先設(shè)置數(shù)據(jù)行的延遲值
    conn.zadd("delay:", delay, rowId);
    //2、立即對(duì)需要行村的數(shù)據(jù)進(jìn)行調(diào)度
    conn.zadd("schedule:", System.currentTimeMillis() / 1000, rowId);
}

--------------------------------------------------------------------------------------

/**
 * 1、通過(guò)組合使用調(diào)度函數(shù)和持續(xù)運(yùn)行緩存函數(shù),實(shí)現(xiàn)類(lèi)一種重讀進(jìn)行調(diào)度的自動(dòng)緩存機(jī)制,
 * 并且可以隨心所欲的控制數(shù)據(jù)行緩存的更新頻率:
 * 2、如果數(shù)據(jù)行記錄的是特價(jià)促銷(xiāo)商品的剩余數(shù)量,并且參與促銷(xiāo)活動(dòng)的用戶(hù)特別多的話,那么最好每隔幾秒更新一次數(shù)據(jù)行緩存:
 * 另一方面,如果數(shù)據(jù)并不經(jīng)常改變,或者商品缺貨是可以接受的,那么可以每隔幾分鐘更新一次緩存。
 */
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、嘗試獲取下一個(gè)需要被緩存的數(shù)據(jù)行以及該行的調(diào)度時(shí)間戳,返回一個(gè)包含0個(gè)或一個(gè)元組列表
            Set range = conn.zrangeWithScores("schedule:", 0, 0);
            Tuple next = range.size() > 0 ? range.iterator().next() : null;
            long now = System.currentTimeMillis() / 1000;
            //2、暫時(shí)沒(méi)有行需要被緩存,休眠50毫秒。
            if (next == null || next.getScore() > now) {
                try {
                    sleep(50);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
                continue;
            }
            //3、提前獲取下一次調(diào)度的延遲時(shí)間,
            String rowId = next.getElement();
            double delay = conn.zscore("delay:", rowId);
            if (delay <= 0) {
                //4、不必在緩存這個(gè)行,將它從緩存中移除
                conn.zrem("delay:", rowId);
                conn.zrem("schedule:", rowId);
                conn.del("inv:" + rowId);
                continue;
            }
            //5、繼續(xù)讀取數(shù)據(jù)行
            Inventory row = Inventory.get(rowId);
            //6、更新調(diào)度時(shí)間,并設(shè)置緩存值。
            conn.zadd("schedule:", now + delay, rowId);
            conn.set("inv:" + rowId, gson.toJson(row));
        }
    }
}

具體的內(nèi)容看我之前寫(xiě)的筆記購(gòu)物網(wǎng)站的redis相關(guān)實(shí)現(xiàn)(Java)和文章投票網(wǎng)站的redis相關(guān)實(shí)現(xiàn)(Java).

這個(gè)Redis主題就算記錄完了,gogogo。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68705.html

相關(guān)文章

  • Java面試 32個(gè)核心必考點(diǎn)完全解析

    摘要:如問(wèn)到是否使用某框架,實(shí)際是是問(wèn)該框架的使用場(chǎng)景,有什么特點(diǎn),和同類(lèi)可框架對(duì)比一系列的問(wèn)題。這兩個(gè)方向的區(qū)分點(diǎn)在于工作方向的側(cè)重點(diǎn)不同。 [TOC] 這是一份來(lái)自嗶哩嗶哩的Java面試Java面試 32個(gè)核心必考點(diǎn)完全解析(完) 課程預(yù)習(xí) 1.1 課程內(nèi)容分為三個(gè)模塊 基礎(chǔ)模塊: 技術(shù)崗位與面試 計(jì)算機(jī)基礎(chǔ) JVM原理 多線程 設(shè)計(jì)模式 數(shù)據(jù)結(jié)構(gòu)與算法 應(yīng)用模塊: 常用工具集 ...

    JiaXinYi 評(píng)論0 收藏0
  • 慕課網(wǎng)_《Redis入門(mén)》學(xué)習(xí)總結(jié)

    摘要:時(shí)間年月日星期日說(shuō)明本文部分內(nèi)容均來(lái)自慕課網(wǎng)。當(dāng)對(duì)應(yīng)的鏈表存在時(shí),從左側(cè)插入數(shù)據(jù)。從右側(cè)插入數(shù)據(jù)。當(dāng)系統(tǒng)在定時(shí)持久化之前出現(xiàn)宕機(jī),還未來(lái)得及往硬盤(pán)寫(xiě)入數(shù)據(jù),那數(shù)據(jù)就丟失了。當(dāng)數(shù)據(jù)集過(guò)大時(shí),可能會(huì)導(dǎo)致服務(wù)器停止幾百毫秒甚至是秒鐘。 時(shí)間:2017年05月21日星期日說(shuō)明:本文部分內(nèi)容均來(lái)自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)示例源碼:無(wú)個(gè)人學(xué)習(xí)源碼:https:...

    leanxi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<