【数据库开发】windows环境下通过c++使用redis

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 edis-2.6。

在D:Redis edis-2.6in elease下 有两个zip包 一个32位一个64位。

根据自己windows的位数 解压到D:Redis edis-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]. ",command1);

        freeReplyObject(r);

        redisFree(c);

         return;

     }

     //由于后面重复使用该变量,所以需要提前释放,否则内存泄漏。

     freeReplyObject(r);

     printf("Succeed toexecute command[%s]. ",command1);

     const char* command2 ="strlen stest1";

     r = (redisReply*)redisCommand(c,command2);

     if (r->type !=REDIS_REPLY_INTEGER) {

        printf("Failed to execute command[%s]. ",command2);

        freeReplyObject(r);

        redisFree(c);

         return;

     }

     int length =r->integer;

     freeReplyObject(r);

     printf("The lengthof 'stest1' is %d. ",length);

     printf("Succeed toexecute command[%s]. ",command2);

     const char* command3 ="get stest1";

     r =(redisReply*)redisCommand(c,command3);

     if (r->type !=REDIS_REPLY_STRING) {

        printf("Failed to execute command[%s]. ",command3);

        freeReplyObject(r);

        redisFree(c);

         return;

     }

     printf("The valueof 'stest1' is %s. ",r->str);

     freeReplyObject(r);

     printf("Succeed toexecute command[%s]. ",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]. ",command4);

        freeReplyObject(r);

        redisFree(c);

         return;

     }

     freeReplyObject(r);

     printf("Succeed toexecute command[%s]. ",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]. ",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. ",childReply->str);

     }

     //对于每一个子应答,无需使用者单独释放,只需释放最外部的redisReply即可。

     freeReplyObject(r);

     printf("Succeed toexecute command[%s]. ",command5);

     printf("Begin totest pipeline. ");

     //该命令只是将待发送的命令写入到上下文对象的输出缓冲区中,直到调用后面的

     //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. ",command1);

         freeReplyObject(reply);

        redisFree(c);

     }

     freeReplyObject(reply);

     printf("Succeed toexecute command[%s] with Pipeline. ",command1);

     if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {

        printf("Failed to execute command[%s] withPipeline. ",command2);

        freeReplyObject(reply);

        redisFree(c);

     }

     freeReplyObject(reply);

     printf("Succeed toexecute command[%s] with Pipeline. ",command2);

     if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {

        printf("Failed to execute command[%s] withPipeline. ",command3);

        freeReplyObject(reply);

        redisFree(c);

     }

     freeReplyObject(reply);

     printf("Succeed toexecute command[%s] with Pipeline. ",command3);

     if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {

        printf("Failed to execute command[%s] withPipeline. ",command4);

        freeReplyObject(reply);

        redisFree(c);

     }

     freeReplyObject(reply);

     printf("Succeed toexecute command[%s] with Pipeline. ",command4);

     if (REDIS_OK !=redisGetReply(c,(void**)&reply)) {

        printf("Failed to execute command[%s] withPipeline. ",command5);

        freeReplyObject(reply);

        redisFree(c);

     }

     freeReplyObject(reply);

     printf("Succeed toexecute command[%s] with Pipeline. ",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.6msvsinstallx32目录
然后编译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

原文地址:https://www.cnblogs.com/huty/p/8517425.html