redis學習小結

http://redisdoc.com/index.html   特別詳細的redis學習文檔

https://github.com/redis/hiredis.git   redis的C語言接口源碼

https://github.com/uglide/RedisDesktopManager.wiki.git   一個redis桌面管理工具,比較遺憾的是不能實時更新,回頭試試改改代碼,做到實時更新

redis是一個高性能的key-value的內存數據庫.也可持久化於硬盤,並且支持多種編程語言的對接.可謂是神通廣大.久聞其大名,一直沒有去抽時間瞭解,終於下定決心去看了一下,發現原來並不難.

當然本人所接觸的無非是一些基本的redis操作和使用.而其最神奇的地方相比是集羣的搭建,而這卻是我所欠缺的.

在使用redis的過程中,有任何命令方面的問題都完全不用藉助任何資料,只需要help一下即可.後面的內容我會提到help的神奇之處.

1. 安裝:

我是ubuntu,直接用的是apt的方式安裝的,別的方式沒有用過,不過網上都有這些教程.

安裝成功以後,執行redis-server即可啓動服務器.然後執行redis-cli啓動redis客戶端,執行ping命令,如果回顯是pong,則表示服務啓動成功.

2. 類型:

首先普及一下,就是貌似所有的教程都是用大寫的命令,其實小寫的命令也是完全可以的.我就不喜歡總是切換大小寫,所以直接用小寫的命令.

redis有五種基本類型:

string : 一對一的方式,可以使用set, mset進行插入或者修改(存在即爲修改,不存在即爲插入),使用append進行追加.strlen獲取字符串長度.在使用的過程中有任何疑問都可以help @string獲取到幫助.

hash : 哈希表.採用key-value的存取方式.使用命令hmset key1 value1 key2 value2 ...進行設置(如果鍵值重複則覆蓋原來的內容,不重複則創建),也可以使用hset(只可以一次設置一個).可以通過hkeys獲取到所有的鍵值,通過hvals獲取到所有的values.hgetall獲取所有的key-value組合.更多內容可以使用help @hash來獲取.

list : 列表.支持左邊操作和右邊操作,lpush爲左邊插入,rpush從右邊插入,支持左右邊的出隊操作.lpop和rpop.出隊一次只能一個.使用lpush或者rpush一次可以入隊多個成員.查看某個範圍內的成員,lrange list begin end,如果把end設定爲-1, 即可表示最後一個元素.更多內容可以通過help @list獲取.

set : 集合.使用sadd進行插入,smembers查看所有的成員.集合提供了一個無序的儲存結構,所有成員在集合內沒有先後.另外集合提供了三個操作,取交集,取並集和取反(在第一個不在第二個的元素).分別是sinter, sunion, sdiff.更多內容可以通過help @set獲取.

sorted-set : 排序集合.排序集合提供了一種積分制的方式保存數據.每個數據綁定一個分值.排序集合根據這個分值對集合內的成員進行查詢操作.比如可以根據從高到低的順序排序等.使用zadd對排序集合進行數據的添加.zadd sorted-set score1 member1 score2 member2... .支持兩種集合間的操作,取交集和取並集.zinter, zunion.另外就是一些圍繞score進行的操作.另外需要注意,默認的排名是從低到高的.比如有三個搜索引擎的排名:sousou : 2 baidu : 6 google : 10.排名是sousou 排第一(0), baidu排第二(1), google牌第三(2).可以通過zrank來查看排名.例如zrank zset google,也可以把排名反過來,即使用zrevrank zset google,這個時候google就排名第一(0)了.還可以查看排名表.使用zrange查看從上倒下的排名表,比如默認排名可以zrange zset 0 2,打印排名前三位的搜索引擎.反過來就是zrevrange zset 0 2.更多內容可以通過使用help @sorted-set獲取.

3. key操作.

這裏的這個key可不是hash裏面那個key,而是面向整個redis數據庫的.如果我這裏用表來形容,想必是更加貼切.

keys pattern : 獲取pattern指定的表名.pattern可以使用通配符*來進行匹配.如果想要獲取所有表,使用keys *

rename old new : 更改key名.

expire key time : 指定超時時長,以秒爲單位.

pexpire key time : 指定超時時長,以毫秒爲單位.

persist key : 刪除超時的表.我很納悶的是,如果超時了這個表自己就會消失,爲什麼還需要這個命令呢.

ttl key : 獲取以秒爲單位的指定表的剩餘時間.如果沒有指定過超時,則剩餘時間爲-1

pttl key : 毫秒級單位的剩餘時間,如果沒有指定超時,則剩餘時間爲-1.

type key : 獲取一個表的類型.即爲上面提到的五種之一.

del key : 刪除指定表.

dump key : 以序列化的方式顯示指定表的內容.

更多內容可以通過help @generic來獲取.

4. 事務

什麼是事務呢,就是一組一起指定的命令.

multi : 如果要使用事務,首先輸入multi表示事務的開始,然後就可以在後面輸入各種命令了.

exec : 當各種命令都輸入完成以後,執行exec命令,即可執行事務.

discard : 如果某個命令打錯了.可以使用discard清空事務,從新輸入.

watch : 事務的原子鎖.如果需要對某個變量進行獨佔式訪問,即可在執行multi之前watch 這個變量.

unwatch : 解鎖

更多內容可以通過help @transactions

5. 發佈/訂閱

我可以說已經掌握了這個功能的用法,但是卻還沒有完全吃透這個功能應用的場景.只知道是用來通信的.

subscribe : 通過這個命令指定一個頻道進行訂閱.

psubscribe : 可以一次性指定多個頻道進行訂閱.

publish : 發佈消息.首先指定之前用subscribe指定的頻道作爲第一個參數,然後輸入想要發佈的內容即可.

unsubscribe : 停止訂閱.

punsubscribe : 也是停止訂閱.支持pattern方式.

對於停止訂閱不是很理解,因爲訂閱是前臺的,如果不想訂閱ctrl+c就行了.

更多內容可以使用help @pubsub來獲取.

6. 連接

上面提到過一個命令ping,這個命令就是連接命令族的一個.用來查看是否已經啓動服務.另外還有幾個命令如下:

auth : 如果一個對redis使用了加密,那麼就需要這個命令來輸入密碼.

select : redis默認有很多數據庫,可以使用select來進行選擇.

quit : 退出redis

echo : 回顯內容.

沒有更多內容了.如果有命令不確定,請輸入help @connection獲取.

7. 腳本

上面提到的事務雖然可以執行一系列的命令,但是並沒有做到原子.腳本解決了這個問題.腳本中的一系列命令是原子執行的.即不會被打斷.

redis的腳本使用的是lua腳本語法.

eval : 執行一個腳本.

evalsha : 執行一個加載後的腳本生成的sha1序列號.

script load : 加載一個腳本.

script flush : 清空腳本緩衝區.

script exists : 驗證一個腳本是否存在

script kill : 殺死一個正在執行的腳本.不過注意到的一點是,因爲腳本是原子的,所以只有在腳本沒有執行過任何的些操作的前提下,才能夠被殺死,否則就會執行完.

關於腳本需要舉一個例子,然後說一下心得.

比如我謝了一個lua的腳本.setget.lua:

local string = redis.call("get", KEYS[1])

redis.call("set", KEYS[2], string)

無論我要加載這個命令還是執行這個命令,都得在命令行模式下使用:

1) 運行: 直接在命令行下執行 redis-cli eval "$(cat setget.lua)" 2 string string1

2) 加載: 直接在命令行下執行 redis-cli script load "$(cat setget.lua)" 然後記錄下sha1序列號,到redis-cli下執行evalsha 序列號 2 string string1

很多人在寫博客的時候並沒有說清楚,如果我們去到redis-cli的模式下,上面的內容就變成了

eval "$(local string = redis.call('get', KEYS[1]) redis.call('set', KEYS[2], string)" 2 string string1

也就是說,redis並不識別cat,如果要在redis的模式先加載一個腳本,那麼引號中的內容就是腳本的內容.

關於lua腳本還有一個需要補充的地方,lua腳本使用KEYS加下標的方式表示key, 下標從1開始.使用ARGS[]加下標的方式表示value, 下標同樣從1開始.當執行一個lua腳本的時候,第一個參數永遠是key的數量,哪怕key數量爲0,也要指定.

關於腳本的更多內容,請使用help @scripting獲取(其實也沒啥能獲取的了)

8. 服務器

這個主題的命令有很多.不一一介紹了(其實我也沒有記住幾個)

比較重要的是config命令:

config set  : 設置一些配置的值.比如密碼就是通過這樣設置的.config set requirepass "password"

config get  : 獲取一些設置的值.可以通過config get *來獲取全部的配置.如果登錄成功了以後,可以通過config get requirepass 獲取密碼.

flushdb : 就在剛剛見識了它的效果,清空了我所有的當前數據庫的表.

flushall : 如果沒有體驗過上面那個命令,這個命令更是你無法承受的重量.全部數據庫的表都會被清空.

info : 獲取服務器信息.

內容很多,不一一列舉了.更多內容參見help @server

9. hiredis

到這裏還有一個內容需要補充,遠程登錄redis的命令是redis-cli -h host -p port -a password

上面的第二個鏈接是hiredis的git鏈接.可以使用git clone獲取hiredis源碼.

下面說一下hiredis的簡單用法.

首先是兩個結構體:

redisContext : 這個結構體是操作redis的句柄.

redisReply : 保存redis命令執行以後的結果.

接下來是四個函數 :

redisContext * redisConnect(char * host, int port);

void * redisCommand(redisContext *, const char *format, ...);

void freeReplyObject(redisReply*);

void redisFree(redisContext*);

當然hiredis還有很多更高端的用法,我沒有去了解,不過有了上面的認識,就可以進行簡單的redis編程了.

簡單的實現一個功能看看效果:

1) 插入一個string,內容是整型;

2) 插入一個string, 內容是字符串

3) 插入一個哈希表,內容既有整型又有字符串.

4) 一次查詢這三張表.

代碼實現如下:

/******************************************************************************
 ** Coypright(C) 2014-2024 () technology Co., Ltd
 **
 ** 文件名 : redistest.c
 ** 版本號 : 1.0
 ** 描  述 : 
 ** 作  者 : cp3alai
 ** 日  期 : 2015.07.01
 ******************************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <hiredis/hiredis.h>

#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6

int main(int argc, char **argv)
{
    redisContext *context;
    redisReply *reply;

    context = redisConnect("localhost", 6379);
    if (NULL == context)
    {
        return -1;
    }

    reply = (redisReply*)redisCommand(context, "set string 123");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("redis set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "set string1 string");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("redis set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "hmset hash alai good jj good");
    if (NULL == reply)
    {
        redisFree(context);
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("hash set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "strlen string");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_INTEGER)
    {
        printf("string's value is %lld\n", reply->integer);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "get string1");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STRING)
    {
        printf("string1's value is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "hgetall hash");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_ARRAY)
    {
        int loop;
        redisReply *item;

        for (loop = 0; loop < reply->elements; loop++)
        {
            item = reply->element[loop];
            printf("hash %s is %s\n", loop % 2 == 0 ? "key" : "value", item->str);
        }
        freeReplyObject(reply);
    }

    redisFree(context);
    return 0;
}

這裏需要注意的是,每一次執行redisCommand都會有malloc或者calloc等分配內存的動作發生,所以每次用完redisReply以後一定要記得釋放空間,不然就會造成內存泄露.

我把每個循環中的釋放空間的動作刪除後,使用內存檢測工具valgrind看一下效果:


ok.關於redis的內容就分享這麼多.其實掌握這些才僅僅算是redis的入門,接下來需要面對的纔是一些更重要的內容,比如備份,比如數據恢復,比如集羣等.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章