首先,Redis是一个开源的使用C语言编写、开源、支持网络、可基于内存亦可持久化的日志型、高性能的Key-Value数据库,并提供多种语言的API。一般开发者对Redis应该都有所耳闻,而Hiredis是一个Redis的C客户端库函数,基本实现了Redis的协议的最小集。在C/C++开发中如果要使用Redis,则Hiredis是比较常用到的。
Redis的安装这里不表,只说常用的Redis操作。
Redis操作
Redis其实就是一种特殊的数据库,这种数据库的存储方式为键值对的存储方式,能够高效地进行数据的存取。
要使用一种数据库,除了安装外,操作上的第一部肯定是连接,redis的远程连接命令为:
redis-cli -h host -p port -a password
从命令中我们可以看到,连接redis和连接mysql差不多,都需要host、端口及密钥。
连接上Redis库后,就可以开始操作我们的数据,其支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
字符串可能是最常用的存储类型,这种数据的存取就是简单的对键进行 set 和 get 操作:
$ set cloudox "boy"
OK
$ get cloudox
"boy"
这里的键是“cloudox”,我存储的值是“boy”,很简单对吧。
hash哈希类型用于存储键值对集合,list列表用来存储的也是集合,但支持一些范围操作,比如:
$ lpush cloudox a
(interger)1
$ lpush cloudox b
(interger)2
$ lpush cloudox c
(interger)3
$ lrange cloudox 0 1
"c" "b"
这里的lpush命令时往列表的队头插入,所以实际上我们得到的列表为:"c" "b" "a"。lrange就根据两个数字表示的闭区间范围来获取元素数组。
set集合是通过哈希表实现的集合,不能往一个键上插入同一个字符串两次。zset是有序集合,也就是在set的基础上,还加上了一个顺序(分数):
$ zadd cloudox 1 a
(interger)1
$ zadd cloudox 1 a
(interger)0
$ zadd cloudox 2 b
(interger)1
$ zadd cloudox 3 c
(interger)1
$ zrangebyscore cloudox 1 2
1) "a"
2) "b"
如上,和set一样,成功插入返回1,否则返回0。两次插入同样的“a”,会返回0。zrangebyscore的用法和list中的lrange差不多,通过数字划定的闭区间范围来获取在该顺序(分数)中的值。
分数为float,可以是正数、负数、0。在查找是,可用 -inf +inf 来表示负无穷和正无穷。
更详细的操作可以参考:
博客:https://blog.csdn.net/softwave/article/details/51084101
和文档:http://redisdoc.com/sorted_set/zrangebyscore.html
Hiredis使用
更多的对Redis的操作还是在代码中,Hiredis就是一个C库函数,提供了基本的操作函数:
比如数据库连接、发送命令、释放资源:
/**连接数据库*/
redisContext *redisConnect(const char *ip, int port);
/**发送命令请求*/
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/*释放资源*/
void freeReplyObject(void *reply);
void redisFree(redisContext *c);
在使用时,一般顺序为先用 redisConnect 连接数据库,然后用 redisCommand 执行命令,执行完后用 freeReplyObject 来释放redisReply对象,最后用 redisFree 来释放整个连接。
命令执行函数返回的其实是一个指向redisReply对象的指针,redisReply对象是存储Redis操作返回结果的结构体:
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
/*命令执行结果的返回类型*/
int type; /* REDIS_REPLY_* */
/*存储执行结果返回为整数*/
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
/*字符串值的长度*/
size_t len; /* Length of string */
/*存储命令执行结果返回是字符串*/
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
/*返回结果是数组的大小*/
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
/*存储执行结果返回是数组*/
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
关于Hiredis原生接口的使用和介绍,这篇博客写的很详细了:https://www.jianshu.com/p/cafb80392fbb
这里提供一个C++封装的接口类,来方便在C++开发中的使用:
#ifndef HIREDISHELPER_H_
#define HIREDISHELPER_H_
#include "hiredis/include/hiredis.h"
#include
#include
using namespace std;
class HiredisHelper {
public:
void Init(const string &ip, int port, const string &auth_str,int timeout = 200) { //ms
m_ip = ip;
m_port = port;
m_timeout = timeout;
m_ctx = NULL;
m_authstring = auth_str;
}
virtual ~HiredisHelper() {
if (m_ctx) {
redisFree(m_ctx);
m_ctx = NULL;
}
}
//调用者需自行调用 freeReplyObject(reply);
redisReply *ExecuteCmd(const char *format, ...);
redisReply* ExecuteCmd(const string &cmd);
private:
int Connect();
private:
int m_timeout;
int m_port;
string m_ip;
string m_authstring;
redisContext* m_ctx;
};
接口类主要提供初始化及命令执行函数,连接操作在执行命令时会检测并连接,对连接的释放会自动进行,但对reply的释放还是需要手动操作,因为需要用后再释放。
具体的实现代码在我的git工程中可以获取,其使用示例如下:
#include
#include
#include
#include "HiredisHelper.h"
using namespace std;
// hiredis官方: https://github.com/redis/hiredis/
#define CHECK_FREE_REDIS_REPLY(reply) \
if (reply) {\
freeReplyObject(reply);\
} else {\
cout << "freeReplyObject Fail" << endl;\
}
int addStrContent(HiredisHelper &redis_conn) {
string m_redis_key = "cloudox";
stringstream ss_cmd;
ss_cmd << "SET " << m_redis_key;
ss_cmd << " " << "boy";
string cmd = ss_cmd.str()
redisReply *reply = redis_conn.ExecuteCmd(cmd);
if (reply) {
cout << "push redis succ " << cmd << endl;
return 0;
} else {
cout << "push redis fail " << cmd << endl;
return -1;
}
CHECK_FREE_REDIS_REPLY(reply);
}
int getStrContent(HiredisHelper &redis_conn, string &item) {
string m_redis_key = "cloudox";
stringstream ss_cmd;
ss_cmd << "GET " << m_redis_key;
string cmd = ss_cmd.str()
redisReply *reply = redis_conn.ExecuteCmd(cmd);
if (reply && reply->type == REDIS_REPLY_STRING) {
item = reply->str;
return 0;
} else {
cout << "get redis fail " << cmd << endl;
return -1;
}
CHECK_FREE_REDIS_REPLY(reply);
}
int addZsetContent(HiredisHelper &redis_conn) {
string m_redis_key = "cloudox";
stringstream ss_cmd;
ss_cmd << "ZADD " << m_redis_key;
ss_cmd << " " << "1" << " " << "boy";
string cmd = ss_cmd.str()
redisReply *reply = redis_conn.ExecuteCmd(cmd);
if (reply) {
cout << "push redis succ " << cmd << endl;
return 0;
} else {
cout << "push redis fail " << cmd << endl;
return -1;
}
CHECK_FREE_REDIS_REPLY(reply);
}
int getZsetContent(HiredisHelper &redis_conn, vector &items) {
string m_redis_key = "cloudox";
stringstream ss_cmd;
ss_cmd << "ZRANGEBYSCORE " << m_redis_key << " -inf " << "5" << " limit 0 10";
// 取负无穷~5分值内的从第一个开始的十个值。
string cmd = ss_cmd.str()
redisReply *reply = redis_conn.ExecuteCmd(cmd);
if (reply && reply->type == REDIS_REPLY_ARRAY) {
cout << "reply size:" << reply->elements << endl;
for (size_t i = 0; i < reply->elements; ++i) {
if (NULL != reply->element[i]) {
redisReply *ele = reply->element[i];
if (ele->type == REDIS_REPLY_INTEGER) {
items.push_back(to_string(ele->integer));// C++11
} else if (ele->type == REDIS_REPLY_STRING) {
string s(ele->str, ele->len);
items.push_back(s);
}
}
}
return 0;
} else {
cout << "get redis fail " << cmd << endl;
return -1;
}
CHECK_FREE_REDIS_REPLY(reply);
}
int main() {
HiredisHelper redis_conn;
string redis_ip = "172.20.0.4";
int redis_port = 6379;
string redis_auth = "crs-fsdfjlskdjflsajdlfas";
redis_conn.Init(redis_ip, redis_port, redis_auth);
// 添加字符串内容
int ret = addStrContent(redis_conn);
// 获取字符串内容
string item;
ret = getStrContent(redis_conn, item);
cout << item << endl;
// 添加有序集合内容
ret = addZsetContent(redis_conn);
// 获取有序集合内容
vector items;
ret = getZsetContent(redis_conn, items);
if (ret == 0) {
for (int i = 0; i < items.size(); ++i) {
cout << items[i] << endl;
}
}
system("pause");// 暂停以显示终端窗口
return 0;
}
提供了对字符串和有序集合两种数据类型的存取操作,其他的也都类似,其实Hiredis及封装类主要是提供了对redis的使用,真正的操作还是靠自己组装命令来执行,毕竟没有做的特别细致,过于细致其实也就不够通用了嘛。
查看作者首页