Redis
什么是Redis?
Redis 是完全开源的,遵守BSD协议,高性能的key-value数据库。
BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。当你发布使用了BSD协议的代码,或者以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:
- 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
- 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
- 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。
BSD代码鼓励代码共享,但需要尊重代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销 售,因此是对商业集成很友好的协议。
很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者 二次开发。
内容截取自:菜鸟教程。
Redis的特点
- Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis 不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis 支持数据的备份,即master-slave模式的数据备份(主从复制)
- Redis 性能极高,读速度为110000次/s,写速度为81000次/s。(一个字,快)
- Redis 的所有操作都是原子性的。单个操作是原子性的,多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- Redis 还支持 publish/subscribe,通知,key过期等待特性。
Redis 的使用
hiredis 相关
安装与编译
https://github.com/redis/hiredis
git clone https://github.com/redis/hiredis.git
tar -xzvf hiredis.tar.gz //解压
cd hiredis //进入目录
make
sudo make install //将可执行程序赋值到/usr/local/bin目录中,当执行程序中就不要输入完整的路径
sudo ldconfig (更新动态库配置文件) /usr/local/lib
make test //测试redis是否编译正确
//编译时需要加上的后缀
g++ xxx.c -o xxx -I /usr/local/include/hiredis -lhiredis
或者直接g++ xxx.cc -lhiredis
安装完成后在需要使用的文件中加入
#include <hiredis/hiredis.h>
Redis 的重要API
连接redis数据库
redisContext* redisConnect(const char *ip, int port)
redisContext* redisConnectWithTimeout(const char *ip, int port, timeval tv)
//redisContext不是线程安全的
typedef struct redisContext
{
int err; /*错误标志,正确连接标志为0,出错时设置为非零常量*/
char errstr[128]; /*存放错误信息的字符串*/
int fd;
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
} redisContext;
示例程序:
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("Error: %s\n", c->errstr);
// handle error
} else {
printf("Can't allocate redis context\n");
}
}
发送请求命令
第一个参数为连接数据库返回的值,剩余的是可变参数,类似printf。
此函数的返回值是void *,但是一般会强制转换为redisReply类型,便于做进一步处理。
void *redisCommand(redisContext *c, const char *format...)
- 如果命令执行错误,返回值为NULL, redisContext 的err字段被设置为非零常量。
- 如果,错误发生,原先的redisContext就不能重复使用,需要重新建立一个新的连接。
- 如果成功执行命令,则标准返回一个 redisReply 类型,该类型结构如下:
typedef struct redisReply
{
int type; /* 测试收到什么样的返回 REDIS_REPLY_* */
long long integer; /* type 是 REDIS_REPLY_INTEGER 类型, integer保存返回的值*/
int len; /* 保存str类型的长度 */
char *str; /* type 是 REDIS_REPLY_ERROR 和 REDIS_REPLY_STRING,str保存返回的值 */
size_t elements; /* type 是 REDIS_REPLY_ARRAY,保存返回多个元素的数量 */
struct redisReply **element; /* 返回多个元素以redisReply对象的形式存放 */
} redisReply;
//type还可以是REDIS_REPLY_NIL,表示返回了一个零对象,没有数据可以访问。
- 通过redisReply 结构体中的type变量可以确定命令执行的情况。
#define REDIS_REPLY_STRING 1 //字符串
#define REDIS_REPLY_ARRAY 2 //数组,例如mget返回值
#define REDIS_REPLY_INTEGER 3 //数字类型
#define REDIS_REPLY_NIL 4 //空
#define REDIS_REPLY_STATUS 5 //状态,例如set成功返回的‘OK’
#define REDIS_REPLY_ERROR 6 //执行失败
-
REDIS_REPLY_STATUS
返回执行结果为状态的命令。比如set命令的返回值的类型是REDIS_REPLY_STATUS,然后只有当返回信息是“OK”时,才表示该命令执行成功。可以通过 reply->str 得到文字信息,通过 reply->len 得到信息长度。
-
REDIS_REPLY_ERROR
返回错误。错误信息可以通过reply->str得到文字信息,通过 reply->len 得到信息长度。 -
REDIS_REPLY_INTEGER
返回整型标识。可以通过 reply->integer 变量得到类型为 long long 的值。 -
REDIS_REPLY_NIL
返回nil对象,说明不存在要访问的数据。 -
REDIS_REPLY_STRING
返回字符串标识。可以通过 reply->str 得到具体值,通过 reply->len 得到信息长度。 -
REDIS_REPLY_ARRAY
返回数据集标识。数据集中元素的数目可以通过 reply->elements 获得,每个元素是个 redisReply 对象,元素值可以通过 reply->element[…index…].* 形式获得,用在获得多个数据结果的操作。
示例
- 图片中的 SET 命令换成 hiredis 的C代码形式如下:
#include <hiredis/hiredis.h>
redisContext * _connect = redisConnect("127.0.0.1",6379);
if(_connect == NULL || _connect -> err )
{
if(_connect)
{
printf("Error: %s \n",_connect -> errstr);
}else{
printf("Can't allocate redis context\n");
}
}
char key[1024]="key-1";
char val[1024]="hello";
printf("SET %s %s\n",key,val);
redisReply* _reply=(redisReply*)redisCommand(_connect,"SET %s %s",key,val);
//set 命令总是返回 ok ,所以此时 type 始终为 REDIS_REPLY_STATUS (5)
//且信息保存在 str 中
if(_reply -> type == REDIS_REPLY_STATUS )
{
printf("%s\n",_reply -> str);
}
freeReplyObject(_reply);
- 图片中的 GET 命令换成 hiredis 的C代码形式如下:
#include <hiredis/hiredis.h>
redisContext * _connect = redisConnect("127.0.0.1",6379);
if(_connect == NULL || _connect -> err )
{
if(_connect)
{
printf("Error: %s \n",_connect -> errstr);
}else{
printf("Can't allocate redis context\n");
}
}
char key[1024]="key-1";
printf("GET %s\n",key);
redisReply* _reply=(redisReply*)redisCommand(_connect,"GET %s",key);
//GET的返回值有三种:
//key 存在时,返回 key 的值
//key 不存在时,返回 nil
//key 不是字符串类型,返回一个错误
if(_reply -> type == REDIS_REPLY_STRING )//存在,有值
{
printf("%s\n",_reply -> str);
}else if(_reply -> type == REDIS_REPLY_NIL)//不存在
{
printf("nil\n");
}else if(_reply -> type == REDIS_REPLY_ERROR)//错误
{
printf("(error) %s\n",_reply -> str);
}
freeReplyObject(_reply);
- 图片中的 DEL 命令换成 hiredis 的C代码形式如下:
#include <hiredis/hiredis.h>
redisContext * _connect = redisConnect("127.0.0.1",6379);
if(_connect == NULL || _connect -> err )
{
if(_connect)
{
printf("Error: %s \n",_connect -> errstr);
}else{
printf("Can't allocate redis context\n");
}
}
char key[1024]="key-1";
printf("DEL %s\n",key);
redisReply* _reply=(redisReply*)redisCommand(_connect,"DEL %s",key);
//DEL的返回值为删除的键的数目
if(_reply -> type == REDIS_REPLY_INTEGER )//数目
{
printf("(integer) %lld\n",_reply -> integer);
}
freeReplyObject(_reply);
- 其余命令的返回情况,类似处理
- 对于 element 和 elements
- 针对多个结果集,对应到 redis-cli 里面的每一行数据
- 注意 element 相当于是保存结果的数组,先判断 type 是不是 REDIS_REPLY_ARRAY
- 而 elements 里保存是的结果数组的长度。
- 调用完要记得调用 freeReplyObject() 把最开始的 结果 释放掉。(redis 自动递归释放)
Redis 的知识点
- Redis 数据库里面的每一个键值对 (key-value pair) 都是由对象 (object) 组成的。
- 数据库键 总是一个字符串对象 (string object)
- 数据库键的值则可以是:
- 字符串对象
- 列表对象 (list object)
- 哈希对象 (hash object)
- 集合对象 (set object)
- 有序集合对象 (sorted set object)