Redis從入門到放棄系列(一) String

Redis從入門到放棄系列(一) String

本文例子基於:5.0.4 字符串是Redis中最多見的數據結構,底層是採用SDS,是能夠修改的字符串,相似ArrayList,採用預分配冗餘空間的方式來減小內存的頻繁分配。java

首先讓咱們來看一下該如何在redis裏面使用字符串這種類型redis

// 將字符串 value 關聯到 key
// 若是key 已經有值了,直接覆蓋 emmmm.跟咱們使用HashMap是否是很像~
// 若是已經存在一個帶有過時時間的key,咱們從新set會將原先的過時時間清除 (重點)
>set key value [expiration EX seconds|PX milliseconds] [NX|XX]

可選參數:[expiration EX seconds|PX milliseconds] [NX|XX]api

咱們發現 set 方法存在兩個可選參數 [expiration EX seconds|PX milliseconds] 跟 [NX|XX]數據結構

>EX seconds:將鍵的過時時間設置爲 seconds 秒。 執行 SET key value EX seconds 的效果等同於執行 SETEX key seconds value 
>PX milliseconds:將鍵的過時時間設置爲 milliseconds 毫秒。 執行 SET key value PX milliseconds 的效果等同於執行 PSETEX key milliseconds value
>NX:只在鍵不存在時, 纔對鍵進行設置操做。 執行 SET key value NX 的效果等同於執行 SETNX key value
>XX:只在鍵已經存在時, 纔對鍵進行設置操做

##代碼示例:app

//對不存在的健設置
>set name "black-search"
OK
>get name 
"black-search"
----------------------------------
//對已經存在的值設置
>set name "new-black-search"
OK
>get name
"new-black-search"
----------------------------------
//使用 ex seconds 
>set name "black-search" ex 10
OK
>get name
"black-search"
//獲取key的有效時間
>ttl name 
(integer) 6
//稍微等一會再執行下面的命令
>get name 
(nil)
----------------------------------
// 使用nx
>set name "black-search" nx
OK
>set name "black-search" nx
(nil)
----------------------------------
// 使用xx
>set name "new-black-search" xx
OK
//讓咱們設置一個不存在的key
>set key value xx
(nil)

至此,redisset的用法先告一段落.編碼


debug object key

咱們在使用redis進行普通的set處理的時候,咱們會使用一下這樣子的方式去設置:spa

>set name "black-search"
OK
// redis提供的調試 api
>debug object name
Value at:0x179d060 refcount:1 encoding:embstr serializedlength:13 lru:14160370 lru_seconds_idle:17

從上面的輸出咱們重點關注一下serializedlength:13,咱們輸入的明明長度就只有12,爲嘛會輸出13呢~ 實際上是redis裏面幫咱們作了處理,redis會在字符的結尾使用'\0'結尾debug

由於傳統C字符串符合ASCII編碼,這種編碼的操做的特色就是:遇零則止 。即,當讀一個字符串時,只要遇到’\0’結尾,就認爲到達末尾,就忽略’\0’結尾之後的全部字符。所以,若是傳統字符串保存圖片,視頻等二進制文件,操做文件時就被截斷了 因此在這裏讀者是否是也像我同樣去實踐了?調試

127.0.0.1:6379> set name "ssssssss\0 asdasdasdasd"
OK
127.0.0.1:6379> get name
"ssssssss0 asdasdasdasd"
127.0.0.1:6379> set name "asdasdasd \\0 asdasdasdasd "
OK
127.0.0.1:6379> get name
"asdasdasd \\0 asdasdasdasd "
127.0.0.1:6379> set name "asdasdasd'\0' asdasdasdasdasd"
OK
127.0.0.1:6379> get name
"asdasdasd'0' asdasdasdasdasd"
127.0.0.1:6379> set name "asdasdasd '\\0'asdasdasd"
OK
127.0.0.1:6379> get name
"asdasdasd '\\0'asdasdasd"

哈哈,其實這裏redis已經作了過濾了呢~code

sds sdscatrepr(sds s, const char *p, size_t len) {
    s = sdscatlen(s,"\"",1);
    while(len--) {
        switch(*p) {
        case '\\':
        case '"':
            s = sdscatprintf(s,"\\%c",*p);
            break;
        case '\n': s = sdscatlen(s,"\\n",2); break;
        case '\r': s = sdscatlen(s,"\\r",2); break;
        case '\t': s = sdscatlen(s,"\\t",2); break;
        case '\a': s = sdscatlen(s,"\\a",2); break;
        case '\b': s = sdscatlen(s,"\\b",2); break;
        default:
            if (isprint(*p))
                s = sdscatprintf(s,"%c",*p);
            else
                s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
            break;
        }
        p++;
    }
    return sdscatlen(s,"\"",1);
}

回到開頭

咱們說字符串相似Java的ArrayList,那麼它每次是怎樣擴容呢?

//sds.h
#define SDS_MAX_PREALLOC (1024*1024)
//sds.c
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    if (avail >= addlen) return s;

    len = sdslen(s);
    sh = (char*)s-sdsHdrSize(oldtype);
    newlen = (len+addlen);
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty space, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        newsh = s_malloc(hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        s_free(sh);
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    sdssetalloc(s, newlen);
    return s;
}

從上面的代碼能夠看到,當字符串長度小於 1M 時,擴容都是加倍現有的空間,若是超過 1M,擴容時一次只會多擴 1M 的空間。(字符串最大長度爲 512M)

應用場景

1.儲存業務數據

>set user:1 '{"id":1,"name":"黑搜丶D","wechat":"black-search"}'

2.自增ID 當 value的值爲整數類型時,redis能夠把它當作是整數同樣進行自增(incr)自減(decr)操做。因爲 redis 全部的操做都是原子性的,因此沒必要擔憂多客戶端鏈接時可能出現的事務問題。

黑搜丶D

相關文章
相關標籤/搜索