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的入門,接下來需要面對的纔是一些更重要的內容,比如備份,比如數據恢復,比如集羣等.