文档章节

windows环境下通过c++使用redis

涩女郎
 涩女郎
发布于 2015/08/23 20:57
字数 2743
阅读 1846
收藏 4

1.Windows下Redis的安装使用

Redis是一个key-value存储系统。Redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。本文中,作者分享了在Windows下进行安装和使用Redis的技巧。

Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sortedset --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave。

前言

因为是初次使用,所以是在windows下进行安装和使用,参考了几篇博客,下面整理一下:

安装Redis

官方网站:http://redis.io/   具体使用redis命令和方法请参考官网

官方下载:http://redis.io/download 可以根据需要下载不同版本

windows版:https://github.com/MSOpenTech/redis/tree/2.6

github的资源可以ZIP直接下载的(这个是给不知道的同学友情提示下)。

下载完成后 可以右键解压到 某个硬盘下 比如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 

 

2.   windows环境下通过c++使用redis

环境: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;

 }

3.将Redis做为windows服务

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就可以直接安装,同时选择安装做为服务了。

1.   测试结果

自己写了一个小程序测试下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

© 著作权归作者所有

共有 人打赏支持
涩女郎
粉丝 35
博文 104
码字总数 160210
作品 0
浦东
高级程序员
私信 提问
在Windows上以服务方式运行 MSOPenTech/Redis

ServiceStack.Redis 使用教程里提到Redis最好还是部署到Linux下去,Windows只是用来做开发环境,现在这个命题发生改变了,在Windows上也可以部署生产环境的Redis,这都要感谢微软的开放,把R...

涩女郎
2015/08/23
0
0
vc++windows编程技术(3)

从一开始就有两种编写windows应用程序的方法。第一种是使用win32定义的API函数,体现细节好。另一种是使用特殊的c++类库,它囊括了API,目前最流行的是MFC。两种方法的区别:所有windows应用...

mli
2014/10/23
0
2
acl 网络通信服务器框架 3.1.0 版本发布

acl 3.1.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三个...

郑树新
2015/02/08
2.9K
2
Visual Studio 2012 Build Clang

不知道还有没有人记得Borland C/C++,C/C++四国圣战中的主角,换了老板 如今还在不冷不热的发展 ,支持Win32/Win64 OSX平台,即Embarcadero C++ Builder 现在支持Windows 64编程 最近试了一下...

Force武装卫队
2013/01/14
0
24
功能强大的 C++ redis 客户端库增加至 acl 项目中

虽然 redis 开发库已有不少,但 C/C++ 的客户端库好用的并不多,虽然官方也提供了 C 版的客户端库,但易用性较差,而且不支持连接池功能,相对于 C/C++ 的库,JAVA 版的 jedis 要好用的多,j...

郑树新
2015/02/04
8.2K
9

没有更多内容

加载失败,请刷新页面

加载更多

二十分钟教你如何将区块链应用与函数计算相结合

前言 本篇文章适合对区块链应用感兴趣或是想要通过函数计算服务进一步开发区块链应用的新人。本文将结合阿里云区块链服务、阿里云函数计算服务、阿里云日志服务 以及社区应用 Marbles,手把手...

阿里云官方博客
6分钟前
1
0
Double数相加后结果不准确

在我们进行两个double运算时,例如:2..0-1.1 不是想象的输出0.9,而是0.89999999999999999。其主要原因是浮点数值采用二进制系统表示,而在二进制系统中无法精确的表示分数1/10。这就好像十...

嘴角轻扬30
14分钟前
1
0
去除移动端点击效果

移动端点击时,会有一个类似active的短暂背景淡出效果,去除该效果可使用 -webkit-tap-highlight-color: rgba(255, 0, 0, 0);

originDu
15分钟前
1
0
腾讯云与MariaDB 基金会签署战略合作,共建全球开源生态圈

本文由云+社区发表 腾讯云日前与MariaDB基金会正式签署战略合作协议,2019年,腾讯云将继续以白金会员身份为基金会的发展提供强有力的资源支持,与MariaDB全球用户和开发者一道,共建开放共赢...

腾讯云加社区
20分钟前
1
0
Kotlin的SAM(Single Abstract Method)

今天有人在群里问kotlin支持SAM的问题,其实kotlin不支持SAM,因为人家支持FP(function programing) package reactinterface Test { fun print()}class TestInterface(var...

SuShine
21分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部