Redis是一個key-value存儲系統。Redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合能夠對關係數據庫起到很好的補充做用。本文中,做者分享了在Windows下進行安裝和使用Redis的技巧。 html
Redis是一個key-value存儲系統。和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sortedset --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,redis支持各類不一樣方式的排序。與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave。 c++
前言 git
由於是初次使用,因此是在windows下進行安裝和使用,參考了幾篇博客,下面整理一下: github
安裝Redis redis
官方網站:http://redis.io/ 具體使用redis命令和方法請參考官網 數據庫
官方下載:http://redis.io/download 能夠根據須要下載不一樣版本 小程序
windows版:https://github.com/MSOpenTech/redis/tree/2.6 windows
github的資源能夠ZIP直接下載的(這個是給不知道的同窗友情提示下)。 api
下載完成後 能夠右鍵解壓到 某個硬盤下 好比D:\Redis\redis-2.6。 數組
在D:\Redis\redis-2.6\bin\release下 有兩個zip包 一個32位一個64位。
根據本身windows的位數 解壓到D:\Redis\redis-2.6根目錄下。
2.啓動Redis
進入redis目錄後 開啓服務 (注意加上redis.conf)
1. redis-server.exe redis.conf
這個窗口要保持開啓 關閉時redis服務會自動關閉
redis會自動保存數據到硬盤 因此圖中是我第二次開啓時 多了一個 DB loaded from disk
3.測試使用
另外開啓一個命令行窗口 進入redis目錄下 (注意修改本身的ip)
1. redis-cli.exe -h 192.168.10.61 -p 6379
環境:VS2010
1. 新建一個Win32 ConsoleApplication工程
2. 將工程屬性->C/C++->CodeGeneration->Runtime Library設置爲Multi-threadedDebug(Debug版本)或Multi-threaded(Release版本)
3. 將hiredis.h文件放到工程目錄下,將hiredis.lib文件放到Debug或Release目錄下,總之讓程序能找到hiredis.lib文件
程序以下:
#include"stdafx.h"
#include"hiredis.h"
#pragmacomment(lib, "hiredis.lib")
#pragmacomment(lib, "ws2_32.lib")
void doTest()
{
//初始化ws2_32庫
WSADATA wsaData;
WSAStartup(MAKEWORD(2,1), &wsaData);
int timeout = 10000;
struct timeval tv;
tv.tv_sec = timeout /1000;
tv.tv_usec = timeout *1000;
//以帶有超時的方式連接Redis服務器,同時獲取與Redis鏈接的上下文對象。
//該對象將用於其後全部與Redis操做的函數。
redisContext* c = redisConnect((char*)"127.0.0.1",6379);
if (c->err) {
redisFree(c);
return;
}
const char* command1 ="set stest1 value9";
redisReply* r =(redisReply*)redisCommand(c,command1);
//須要注意的是,若是返回的對象是NULL,則表示客戶端和服務器之間出現嚴重錯誤,必須從新連接。
//這裏只是舉例說明,簡便起見,後面的命令就再也不作這樣的判斷了。
if (NULL == r) {
redisFree(c);
return;
}
//不一樣的Redis命令返回的數據類型不一樣,在獲取以前須要先判斷它的實際類型。
//至於各類命令的返回值信息,能夠參考Redis的官方文檔,或者查看該系列博客的前幾篇
//有關Redis各類數據類型的博客。:)
//字符串類型的set命令的返回值的類型是REDIS_REPLY_STATUS,而後只有當返回信息是"OK"
//時,才表示該命令執行成功。後面的例子以此類推,就再也不過多贅述了。
if (!(r->type == REDIS_REPLY_STATUS &&(strcmp(r->str,"OK")== 0 || strcmp(r->str, "ok") == 0))) {
printf("Failed to execute command[%s].\n",command1);
freeReplyObject(r);
redisFree(c);
return;
}
//因爲後面重複使用該變量,因此須要提早釋放,不然內存泄漏。
freeReplyObject(r);
printf("Succeed toexecute command[%s].\n",command1);
const char* command2 ="strlen stest1";
r = (redisReply*)redisCommand(c,command2);
if (r->type !=REDIS_REPLY_INTEGER) {
printf("Failed to execute command[%s].\n",command2);
freeReplyObject(r);
redisFree(c);
return;
}
int length =r->integer;
freeReplyObject(r);
printf("The lengthof 'stest1' is %d.\n",length);
printf("Succeed toexecute command[%s].\n",command2);
const char* command3 ="get stest1";
r =(redisReply*)redisCommand(c,command3);
if (r->type !=REDIS_REPLY_STRING) {
printf("Failed to execute command[%s].\n",command3);
freeReplyObject(r);
redisFree(c);
return;
}
printf("The valueof 'stest1' is %s.\n",r->str);
freeReplyObject(r);
printf("Succeed toexecute command[%s].\n",command3);
const char* command4 ="get stest2";
r =(redisReply*)redisCommand(c,command4);
//這裏須要先說明一下,因爲stest2鍵並不存在,所以Redis會返回空結果,這裏只是爲了演示。
if (r->type !=REDIS_REPLY_NIL) {
printf("Failed to execute command[%s].\n",command4);
freeReplyObject(r);
redisFree(c);
return;
}
freeReplyObject(r);
printf("Succeed toexecute command[%s].\n",command4);
const char* command5 ="mget stest1 stest2";
r = (redisReply*)redisCommand(c,command5);
//不論stest2存在與否,Redis都會給出結果,只是第二個值爲nil。
//因爲有多個值返回,由於返回應答的類型是數組類型。
if (r->type !=REDIS_REPLY_ARRAY) {
printf("Failed to execute command[%s].\n",command5);
freeReplyObject(r);
redisFree(c);
//r->elements表示子元素的數量,無論請求的key是否存在,該值都等於請求是鍵的數量。
assert(2== r->elements);
return;
}
int i;
for (i = 0; i <r->elements; ++i) {
redisReply* childReply = r->element[i];
//以前已經介紹過,get命令返回的數據類型是string。
//對於不存在key的返回值,其類型爲REDIS_REPLY_NIL。
if(childReply->type == REDIS_REPLY_STRING)
printf("The value is %s.\n",childReply->str);
}
//對於每個子應答,無需使用者單獨釋放,只需釋放最外部的redisReply便可。
freeReplyObject(r);
printf("Succeed toexecute command[%s].\n",command5);
printf("Begin totest pipeline.\n");
//該命令只是將待發送的命令寫入到上下文對象的輸出緩衝區中,直到調用後面的
//redisGetReply命令纔會批量將緩衝區中的命令寫出到Redis服務器。這樣能夠
//有效的減小客戶端與服務器之間的同步等候時間,以及網絡IO引發的延遲。
//至於管線的具體性能優點,能夠考慮該系列博客中的管線主題。
/* if (REDIS_OK !=redisAppendCommand(c,command1)
||REDIS_OK != redisAppendCommand(c,command2)
||REDIS_OK != redisAppendCommand(c,command3)
||REDIS_OK != redisAppendCommand(c,command4)
||REDIS_OK != redisAppendCommand(c,command5)) {
redisFree(c);
return;
}
*/
redisAppendCommand(c,command1);
redisAppendCommand(c,command2);
redisAppendCommand(c,command3);
redisAppendCommand(c,command4);
redisAppendCommand(c,command5);
redisReply* reply =NULL;
//對pipeline返回結果的處理方式,和前面代碼的處理方式徹底一直,這裏就再也不重複給出了。
if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {
printf("Failed to execute command[%s] withPipeline.\n",command1);
freeReplyObject(reply);
redisFree(c);
}
freeReplyObject(reply);
printf("Succeed toexecute command[%s] with Pipeline.\n",command1);
if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {
printf("Failed to execute command[%s] withPipeline.\n",command2);
freeReplyObject(reply);
redisFree(c);
}
freeReplyObject(reply);
printf("Succeed toexecute command[%s] with Pipeline.\n",command2);
if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {
printf("Failed to execute command[%s] withPipeline.\n",command3);
freeReplyObject(reply);
redisFree(c);
}
freeReplyObject(reply);
printf("Succeed toexecute command[%s] with Pipeline.\n",command3);
if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {
printf("Failed to execute command[%s] withPipeline.\n",command4);
freeReplyObject(reply);
redisFree(c);
}
freeReplyObject(reply);
printf("Succeed toexecute command[%s] with Pipeline.\n",command4);
if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {
printf("Failed to execute command[%s] withPipeline.\n",command5);
freeReplyObject(reply);
redisFree(c);
}
freeReplyObject(reply);
printf("Succeed toexecute command[%s] with Pipeline.\n",command5);
//因爲全部經過pipeline提交的命令結果均已爲返回,若是此時繼續調用redisGetReply,
//將會致使該函數阻塞並掛起當前線程,直到有新的經過管線提交的命令結果返回。
//最後不要忘記在退出前釋放當前鏈接的上下文對象。
redisFree(c);
return;
}
int main()
{
doTest();
return 0;
}
Redis的服務器運行時總有個窗口, 感受很討厭,發佈軟件給人的感受不正規,本打算本身寫個服務程序,把Redis-server封裝一下,但是發現一篇文章。http://www.cnblogs.com/shanyou/archive/2013/01/17/redis-on-windows.html,原來人家早就有這個功能了。
從https://github.com/MSOpenTech/redis下載了一份Redis 2.6
打算編譯一個RedisWatcher,使Redis作爲Windows的服務運行。
編譯失敗。
須要安裝Wix,在http://wix.codeplex.com/下載了一個3.8的版本。
安裝後進入VS2010從新編譯,出現錯誤LGHT0094,Google了很久,終於找到辦法
<EnableProjectHarvesting>True</EnableProjectHarvesting> ,編譯成功。
個人系統是XP,運行InstallWatcher.msi,安裝成功,在服務裏也出現了redis watcher啓動類型爲自動。
運行服務失敗。1053 服務沒有及時響應啓動或控制請求。
重啓系統也沒用什麼做用。
在Win7下安裝InstallWatcher.msi正常,啓動服務正常,Redis也能夠正常使用。
開始覺得是Wix版本過高,降回到3.6仍是老樣子。
實在沒有辦法了,用Dependency Walker看了一下,發現使用ADVAPI32.DLL中的EventRegister、EventUnregister、EventWrite三個函數,而個人系統中的advapi32.dll沒有這三個函數。查了一下,發現這幾個函數要求的最低版本是WidnowsVista。
沒辦法,手工把這幾個函數去掉吧。
在RedisWatcher.h中,將使用這幾個函數的位置都直接註釋掉,直接返回ERROR_SUCCESS。
編譯……成功,而後VS2010會提示,RedisWatcher.h在外部被修改,是否須要更新,更新進來一看。
RedisWatcher.h又回覆了我修改以前的狀態。
編譯出來的exe文件仍是老樣子
反覆了幾回,這個文件仍是會被自動恢復。
實在沒有辦法,用Filemon監控哪些進程讀寫了RedisWatcher.h,大部分都是cl.exe和devenv.exe,偶然發現有個mc.exe
查了一下,發現這個很奇妙的東西。
http://technet.microsoft.com/zh-cn/library/aa385638
在服務程序中將服務的運行的狀態寫到日誌裏,這時就要本身生成一個消息表,將這些消息放到程序裏,用ReportEvent就能將記錄寫到日誌裏
MC.exe是一個能夠生成消息資源文件的工具,生成後的文件能夠供應用程序或者DLL使用。
會自動生成*.h,*.rc,*.bin 。
在工程的設置裏BuildEvents->Pre-Build Event中存在
mc -um $(ProjectName).man -h "$(ProjectDir)\" -z $(ProjectName)
大體看了下mc的文檔,發現有個-mof參數,能夠生成支持vsita之前的版本。
mc -um -mof $(ProjectName).man -h "$(ProjectDir)\" -z $(ProjectName)
再次生成的RedisWatcher.h的確沒有了那三個函數,但是編譯出了一堆錯誤。也沒有心情和時間去細研究Wix和mc,就把正常生成的RedisWatcher.h裏自動生成的關於寫日誌的函數全都去掉。再把mc也去掉。
此次編譯成功了,安裝後服務也能正常啓動。暫時算是成功。
把
InstallWatcher.msi
redis-benchmark.exe
redis-check-aof.exe
redis-check-dump.exe
redis-cli.exe
redis-server.exe
redis.conf
RedisWatcher.exe
RedisWatcher.man
watcher.conf
這些文件複製到redis-2.6\msvs\install\x32目錄
而後編譯RedisInstall.sln,將會直接生成一個RedisInstall32.msi就能夠直接安裝,同時選擇安裝作爲服務了。
本身寫了一個小程序測試下redis數據庫的速度以下:
執行100000次hmset user%d key1 value1key2 value2命令用時6.8秒左右
執行1000000次hmset user%d key1value1 key2 value2命令用時70.9秒左右
參考資料:
1.Windows下Redis的安裝使用
http://os.51cto.com/art/201403/431103.htm
2.c++使用redis
http://www.360doc.com/content/13/0606/11/10072361_290882627.shtml
3.Redis作爲windows服務的曲折過程
http://blog.csdn.net/yuanyingtanxi/article/details/17145163