C/C++编程操作Redis数据库,hiredis包装redis数据库操作接口及测试(增删改查与连接)
介绍一个实际应用场景,对于客户频繁需要查询的信息,可以将其放在redis内存数据库中,相当于一个缓存,每次查的时候先去redis内存数据库中去查询,如果查询不到再去oracle数据库中查询,这样提高了效率。
本文,使用redis的C与语操作接口hiredis包装了redis数据库的增删改查以及连接和断开连接接口,可以用在实际应用中。
C/C++接口库下载
git clone https://github.com/redis/hiredis.git
一、hiredis介绍
hiredis提供了一linux下访问redis的接口,比如连接和操作redis数据库。这里介绍本文要使用的几个api,其他的可参考官网介绍。
redisContext *redisConnect(const char *ip, int port); /*连接redis*/
void *redisCommand(redisContext *c, const char *format, ...); /*redis数据库操作 如SET GET AUTH等*/
void freeReplyObject(void *reply); /*释放redisCommand返回得到的结果结构体*
void redisFree(redisContext *c); /*释放连接上下文断开连接*/
hiredis中两种比较重要的结构体定义如下:
typedef struct redisContext
{
int err;
char errstr[128];
int fd;
int flags;
char *obuf;
redisReader *reader;
} redisContext;typedef struct redisReply
{
int type;
long long integer;
int len;
char *str;
size_t elements;
struct redisReply **element;
} redisReply;
在调用redisConnect连接数据库时候,会返回一个redisContext结构体指针,可以看做是一个连接句柄,redisCommand需要用到该句柄。调用redisCommand返回一个redisReply指针(实际返回的类型是void*,需要显示的转换为redisReply*指针),指向的结构体包含了一些返回信息,如返回类型、返回值和长度等。
二、hiredis使用
(1)连接redis
调用redisConnect连接redis数据库,得到一个redisContext*指针,如果连接失败,则结构体中的err和errstr被置值。连接成功则err为0。使用示例如下:
redisContext *c = NULL;
/*connect*/
c = redisConnect(ip, port);
(2)操作redis
使用redisCommand,该接口返回一个void*指针,显式转换为redisReply*指针,指向的结构中包含了一些返回信息,比较重要的是返回类型,代表了返回结果的类型。使用示例如下:
redisReply *reply = NULL;
reply = (redisReply *)redisCommand(c, "GET %s", key);
reply->type表明了返回的类型,常见的类型如下:
REDIS_REPLY_STATUS:
返回了命令执行状态,状态信息可以由reply-> str获得。AUTH命令和SET命令正常情况下返回该值, reply-> str为OK。
REDIS_REPLY_ERROR:
表示执行出现了一个错误
REDIS_REPLY_INTEGER:
返回了一个整数,整数值由eply->integer表示。例如DEL操作,如果key存在,正常情况下返回该类型,表示键的个数。
REDIS_REPLY_NIL:
返回了nil。 表示无相应数据,例如GET不存在的key。
REDIS_REPLY_STRING:
表示命令执行后返回了一个字符串。 返回的字符串使用reply-> str来获取。 这个字 符串的长度为reply-> len。例如GET,键存在的情况下,返回一个字符串。
(3)释放返回结果机构体
freeReplyObject(reply)
使用完毕后需要释放reply机构体。
(4)断开连接,清空redisContext
redisFree(c);
三、hiredis包装redis数据库的增删改查、连接和断开连接接口函数,接口函数如下:
redisContext *redis_connect(char *ip, unsigned int port, char *passwd);
void redis_disconnect(redisContext *c);
int redis_insert(redisContext *c, char *key, char *value, int ex);
int redis_delete(redisContext *c, char *key);
int redis_select(redisContext *c, char *key, char *value);int redis_update(redisContext *c, char *key, char *value, int ex);
具体实现:
1.redisopr.h
#ifndef _REDISOPR_H_
#define _REDISOPR_H_//string
redisContext *redis_connect(char *ip, unsigned int port, char *passwd);
void redis_disconnect(redisContext *c);
void redis_disconnect(redisContext *c);
int redis_insert(redisContext *c, char *key, char *value, int ex);
int redis_delete(redisContext *c, char *key);
int redis_select(redisContext *c, char *key, char *value);
int redis_update(redisContext *c, char *key, char *value, int ex);//...
#endif
2. redisopr.c
/********************************
* redis opration
* created time: 2018-01-01
* created by poetteaes
*******************************/
# include "hiredis/hiredis.h"
# include <stdlib.h>
# include <string.h>
# define NOT_FOUND -1403
/********************************
* redis connect
* return a pointer if success;
* return NULL if failed
*******************************/
redisContext *redis_connect(char *ip, unsigned int port, char *passwd)
{
redisContext *c = NULL;
redisReply *replay = NULL;
/*connect*/
c = redisConnect(ip, port);
if(c == NULL)
{
printf("Error: redisConnect() error!\n");
return NULL;
}
if(c->err != 0)
{
printf("Error: %s\n", c->errstr);
redisFree(c);
}
/*auth if passwd is not NULL*/
if(passwd != NULL)
{
replay = (redisReply *)redisCommand(c, "AUTH %s", passwd);
if( replay == NULL)
{
printf("Error: AUTH error!\n");
redisFree(c);
printf("redisFree\n");
return NULL;
}
if( !(replay->type == REDIS_REPLY_STATUS && memcmp(replay->str, "OK", 2) == 0) )
{
printf("Error: AUTH error!\n");
freeReplyObject(replay);
redisFree(c);
printf("redisFree\n");
return NULL;
}
}
return c; /*connect success*/
}
/********************************
* redis select
* return length of value if success;
* return NOT_FOUND if not found
* return -1 if failed
*******************************/
int redis_select(redisContext *c, char *key, char *value)
{
redisReply *reply = NULL;
if(value == NULL)
{
printf("Error: Invalid output argument!\n");
return -1;
}
reply = (redisReply *)redisCommand(c, "GET %s", key);
if( reply == NULL)
{
printf("Error: GET error!\n");
return -1;
}
if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_NIL)
{
printf("Error: GET error!\n");
freeReplyObject(reply);
return -1;
}
if(reply->type == REDIS_REPLY_NIL)
{
printf("Not found\n");
freeReplyObject(reply);
return NOT_FOUND;
}
memcpy(value, reply->str, reply->len);
freeReplyObject(reply);
return (reply->len);
}
/********************************
* redis insert
* return 0 if success;
* return -1 if failed
*******************************/
int redis_insert(redisContext *c, char *key, char *value, int ex)
{
redisReply *reply = NULL;
if(ex < 0)
{
printf("Error: Invalid input argument ex[%d]", ex);
return -1;
}
/*test if the key has been existed*/
reply = (redisReply *)redisCommand(c, "GET %s", key);
if( reply == NULL)
{
printf("Error: GET error!\n");
return -1;
}
if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_NIL)
{
printf("Error: GET error!\n");
freeReplyObject(reply);
return -1;
}
if(reply->type == REDIS_REPLY_STRING)
{
printf("Error: The key has existed.\n");
freeReplyObject(reply);
return -1;
}
freeReplyObject(reply);
/*insert*/
reply = NULL;
if(ex != 0 )
{
reply = (redisReply *)redisCommand(c, "SET %s %s EX %d", key, value, ex);
}
else
{
reply = (redisReply *)redisCommand(c, "SET %s %s", key, value); /*without ex*/
}
if( reply == NULL)
{
printf("Error: SET error!\n");
return -1;
}
if( !(reply->type == REDIS_REPLY_STATUS && memcmp(reply->str, "OK", 2) == 0) )
{
printf("Error: SET error!\n");
freeReplyObject(reply);
return -1;
}
freeReplyObject(reply);
return 0;
}
/********************************
* redis delete
* return 0 if success;
* return NOT_FOUND if not found
* return -1 if failed
*******************************/
int redis_delete(redisContext *c, char *key)
{
redisReply *reply = NULL;
reply = (redisReply *)redisCommand(c, "DEL %s", key);
if(reply == NULL || reply->type != REDIS_REPLY_INTEGER)
{
printf("Error: The key doesn't exist.\n");
freeReplyObject(reply);
return -1;
}
freeReplyObject(reply);
if(reply->integer == 0)
return NOT_FOUND;
else
return (reply->integer - 1);
}
/********************************
* redis delete
* return 0 if success;
* return NOT_FOUND if not found
* return -1 if failed
*******************************/
int redis_update(redisContext *c, char *key, char *value, int ex)
{
redisReply *reply = NULL;
if(ex < 0)
{
printf("Error: Invalid input argument ex[%d]", ex);
return -1;
}
/*test if the key has been existed*/
reply = (redisReply *)redisCommand(c, "GET %s", key);
if( reply == NULL)
{
printf("Error: GET error!\n");
return -1;
}
if(reply->type != REDIS_REPLY_STRING && reply->type != REDIS_REPLY_NIL)
{
printf("Error: GET error!\n");
freeReplyObject(reply);
return -1;
}
if(reply->type == REDIS_REPLY_NIL)
{
printf("The key doesn't exist.\n");
freeReplyObject(reply);
return NOT_FOUND;
}
freeReplyObject(reply);
/*update*/
reply = NULL;
if(ex != 0)
{
reply = (redisReply *)redisCommand(c, "SET %s %s EX %d", key, value, ex);
}
else
{
reply = (redisReply *)redisCommand(c, "SET %s %s", key, value); /*without ex*/
}
if( reply == NULL)
{
printf("Error: SET error!\n");
return -1;
}
if( !(reply->type == REDIS_REPLY_STATUS && memcmp(reply->str, "OK", 2) == 0) )
{
printf("Error: SET error!\n");
freeReplyObject(reply);
return -1;
}
freeReplyObject(reply);
return 0;
}
/*redis disconnect*/
void redis_disconnect(redisContext *c)
{
redisFree(c);
}
2. redisTest.c
测试程序如下:
/********************************
* redis opration test
* created time: 2018-01-01
* created by poetteaes
*******************************/
# include "hiredis/hiredis.h"
# include <stdlib.h>
# include <string.h>
# include "redisopr.h"
# define EX_TIME 3600
# define NOT_FOUND -1403
int main(void)
{
int ret;
char ip[] = "127.0.0.1";
unsigned int port = 6379;
redisContext *c = NULL;
char key[128] = {0};
char value[128] = {0};
memcpy(key, "key1", 4);
memcpy(value, "value1", 6);
/*connect*/
c = redis_connect(ip, port, NULL);
if(c == NULL || c->err != 0)
{
printf("Error: redis_connect() error!\n");
if(c != NULL )
redis_disconnect(c);
return -1;
}
else
{
printf("redis connected.\n\n");
}
/*insert*/
ret = redis_insert(c, key, value, EX_TIME);
if(ret < 0)
{
printf("Error: redis_insert() error!\n\n");
//redis_disconnect(c);
//return -1;
}
else
{
printf("\nredis_insert() success.\n");
}
/*update*/
ret = redis_update(c, key, "value1update", EX_TIME);
if(ret != 0 && ret != NOT_FOUND)
{
printf("\nError: redis_update() error!\n");
//redis_disconnect(c);
//return -1;
}
else if(ret == NOT_FOUND)
{
printf("key is not found!\n");
}
else
{
printf("\nredis_update() success.\n\n");
}
/*select*/
ret = redis_select(c, key, value);
if(ret < 0 && ret!=NOT_FOUND)
{
printf("\nError: redis_select() error!\n");
//redis_disconnect(c);
//return -1;
}
else if(ret == NOT_FOUND)
{
printf("key is not found!\n");
}
else
{
printf("\nredis_select() success.\n");
printf("value[%s]\n\n",value);
}*/
/*delete*/
ret = redis_delete(c, key);
if(ret < 0 && ret!=NOT_FOUND)
{
printf("\nError: redis_delete() error!\n");
//redis_disconnect(c);
//return -1;
}
else if(ret == NOT_FOUND)
{
printf("key is not found!\n");
}
else
{
printf("\nredis_delete() success.\n");
}
/*disconnect*/
redis_disconnect(c);
printf("\nredis disconnected.\n");
return 0;
}
编译:
gcc redisopr.c redistest.c -o redistest-L/usr/local/lib/ -lhiredis
如果出现:error while loading shared libraries: libhiredis.so.0.14: cannot open shared object file: No such file or directory
那就表示系统不知道xxx.so 放在哪个目录下。
这个时候就要在/etc/ld.so.conf中加入xxx.so所在的目录。
一般而言,有很多so档会在/usr/local/lib这个目录下
在/etc/ld.so.conf中加入/usr/local/lib这一行后,执行 /sbin/ldconfig 命令来更新生效。
接下来可以成功执行了
运行结果:
包装的接口支持对插入时主键重复(报错)以及更新和查找时键不存在的情况进行了判断。例如键已经存在的情况下执行测试程序,结果如下:
四、接口的实际应用
这里只采用对一个键进行操作的API来包装接口,实际上利用hiredis的API,也可以对多个键操作。但是一个键操作包装出来的增删改查在一些应用场景下已足矣,因为多个键实际上可以拼成一个键,比如定长格式的字符串或者JSON字符串,或者分割符字符串,同样结果的字符串也可以拼接,作为拼接key的拼接value,实际使用的时候,对结果再进行解析即可。
再回到开头说的应用场景,设想一个oracle数据库表,为订单表,主键为date和orderid,非主键字段为price何goodsname,交易发生时数据存储到oracle数据库,同时也存储在redis内存数据库中(超时清理),客户查询时候先查redis内存数据库,查不到再查oracle数据库。尽管主键和查询结果都有两个字段,但是完全可以用|分割符将主键的两个字段进行拼接(当然也可以使用JSON等格式封装),作为redis的键,同样结果字段也可以这样拼接,这样表中的每一行数据都可以在redis中由单一的key和value表示。插入redis数据库的时候,把date和orderid用|拼接成键,price何goodsname用|拼接成value,然后调用上述包装的redis_insert函数进行插入;查询redis数据库的时候,把查询字段date和orderid用|拼接为键,使用包装的redis_select去查询,得到拼装的value结果,从中解析出price何goodsname记得到了各个结果字段。
C/C++编程操作Redis数据库,hiredis包装redis数据库操作接口及测试(增删改查与连接)相关推荐
- Redis概述_使用命令对redis的数据进行增删改查_Jedis连接redis进行数据操作_redis进行数据缓存案例
学习目标 redis 概念 下载安装 命令操作 1. 数据结构 持久化操作 使用Java客户端操作redis Redis 前言(从百度上抄的, 看看了解一下, 懒得排版了) 1. 概念: redis是 ...
- Go操作mysql实现增删改查及连接池
[-] 下载驱动 创建测试表 数据库连接 插入操作 查询操作 修改操作 删除操作 完整代码 小结 开启web服务 db对象初始化 请求方法 小结 golang本身没有提供连接mysql的驱动,但是定义 ...
- 若依前后端分离版怎样根据数据库生成代码并快速实现某业务的增删改查
场景 使用若依的前后端分离版,怎样使用其代码生成实现对单表的增删改查导出的业务. 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程 ...
- neo4j图数据库安装(mac)+neo4j集成springboot实现基础的增删改查
目录 第一部分 mac安装neo4j 第二部分 neo4j集成springboot实现基础的增删改查 一.图数据库相关配置 二.业务逻辑 实体类 持久层 业务层 表现层 启动类 三.测试 附录: 第一 ...
- mysql数据库实验+cmd界面运行基本操作总结(sql:数据增删改查,表格,视图,备份恢复)
文章目录 mysql数据库实验--建表准备 1.dos界面中数据库的登录操作 #这里解决一个bug 2.数据库及表格创建 (1)利用sql语句创建数据库DBtest (2)表格创建 3.导入数据至数据 ...
- java连接mysql数据库增删改查_java连接mysql数据库增删改查操作记录
1. 连接数据库.得到数据库连接变量 注意连接数据库的时候 (1)打开DB Browser 新建一个Database Driver,注意加入Driver JARs的时候加入的包,我的是mysql-co ...
- python对sqlite增删改查_Python操作SQLite数据库的方法详解【导入,创建,游标,增删改查等】...
本文实例讲述了python操作SQLite数据库的方法.分享给大家供大家参考,具体如下: SQLite简介 SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的 ...
- wps连接mysql数据库增删改查_python 连接mysql数据库 进行增删改查操作
1.在进行连接之前我们要确定我们已经安装了python和mysql(开玩笑,没有这个你怎么连接那)至于安装的过程在此略过, 2.因为要进行连接mysqldb 所有我们要导入MySQLdb的模块,当然这 ...
- MySQL数据库5.7版本安装部署及常规命令 增删改查 索引 事务 优化
概述:数据库(Database)是按照数据结构组织.存储和管理数据的仓库,它产生于距今六十多年前,数据库有很多种类型,从最简单的存储有各种数据的表格到能够进行海量数据存储的大型数据库系统,在各个方面得 ...
最新文章
- Nature Plants:根系微生物可以远程提高植物应对地上部环境胁迫的能力
- idea servlet自动配置web.xml_Spring Boot学习04_嵌入式Servlet容器自动配置原理
- 无埋点数据收集和adb monkey测试屏蔽通知栏
- 走向DBA[MSSQL篇] - 从SQL语句的角度提高数据库的访问性能
- android 索引怎么使用情况,android 數據庫查詢中使用索引-大幅提高數據庫操作速度...
- Shell配置_配置IP
- php 跨二级域名 设置cookie
- STM32CubeMx官网下载HAL库文档资料
- 在CSS中clear属性的妙用
- python中集合用法大全
- ScheduledThreadPoolExecutor部分源码学习
- 网络寻宝 v2.2 官网
- 话说软件详细设计工具
- swfupload 无法加载_文件上传插件SWFUpload的使用指南
- 边框给背景图css怎么写,使用css设置边框背景图片
- k8s-liveness和readness详解
- JVM之Parallel Scavenge回收器
- CondConv: Conditionally Parameterized Convolutions for Efficient Inference论文解读
- LRU算法,走迷宫,数根,星际战争
- JS内置对象操作方法整理
热门文章
- 【原型设计】第三节:Axure RP9 母版的使用说明以及操作教程
- Keil | 使用Register Windows测量函数的执行时间
- 二叉树层次遍历python_根据二叉树层序遍历顺序(数组),将其转换为二叉树(Python)...
- springmvc登录拦截器访问报错 SyntaxError: expected expression, got ''
- PHP笔记-表格及分页功能
- Windows破解笔记-windows API中的SendMessage
- Qt工作笔记-对主事件循环的进一步认识
- Qt5.7+Opencv2.4.9人脸识别(六)Tcp,Mysql,3DES,XML综合
- DJS_130小型计算机,我收藏的中国第一台计算机djs-130的操作系统纸带
- ERRORS:*: (auth.E003) ‘User.username‘ must be unique because it is named as the ‘USERNAME_FIELD