redis学习小结

http://redisdoc.com/index.html   特别详细的redis学习文档

https://github.com/redis/hiredis.git   redis的C语言接口源码

https://github.com/uglide/RedisDesktopManager.wiki.git   一个redis桌面管理工具,比较遗憾的是不能实时更新,回头试试改改代码,做到实时更新

redis是一个高性能的key-value的内存数据库.也可持久化于硬盘,并且支持多种编程语言的对接.可谓是神通广大.久闻其大名,一直没有去抽时间了解,终于下定决心去看了一下,发现原来并不难.

当然本人所接触的无非是一些基本的redis操作和使用.而其最神奇的地方相比是集群的搭建,而这却是我所欠缺的.

在使用redis的过程中,有任何命令方面的问题都完全不用借助任何资料,只需要help一下即可.后面的内容我会提到help的神奇之处.

1. 安装:

我是ubuntu,直接用的是apt的方式安装的,别的方式没有用过,不过网上都有这些教程.

安装成功以后,执行redis-server即可启动服务器.然后执行redis-cli启动redis客户端,执行ping命令,如果回显是pong,则表示服务启动成功.

2. 类型:

首先普及一下,就是貌似所有的教程都是用大写的命令,其实小写的命令也是完全可以的.我就不喜欢总是切换大小写,所以直接用小写的命令.

redis有五种基本类型:

string : 一对一的方式,可以使用set, mset进行插入或者修改(存在即为修改,不存在即为插入),使用append进行追加.strlen获取字符串长度.在使用的过程中有任何疑问都可以help @string获取到帮助.

hash : 哈希表.采用key-value的存取方式.使用命令hmset key1 value1 key2 value2 ...进行设置(如果键值重复则覆盖原来的内容,不重复则创建),也可以使用hset(只可以一次设置一个).可以通过hkeys获取到所有的键值,通过hvals获取到所有的values.hgetall获取所有的key-value组合.更多内容可以使用help @hash来获取.

list : 列表.支持左边操作和右边操作,lpush为左边插入,rpush从右边插入,支持左右边的出队操作.lpop和rpop.出队一次只能一个.使用lpush或者rpush一次可以入队多个成员.查看某个范围内的成员,lrange list begin end,如果把end设定为-1, 即可表示最后一个元素.更多内容可以通过help @list获取.

set : 集合.使用sadd进行插入,smembers查看所有的成员.集合提供了一个无序的储存结构,所有成员在集合内没有先后.另外集合提供了三个操作,取交集,取并集和取反(在第一个不在第二个的元素).分别是sinter, sunion, sdiff.更多内容可以通过help @set获取.

sorted-set : 排序集合.排序集合提供了一种积分制的方式保存数据.每个数据绑定一个分值.排序集合根据这个分值对集合内的成员进行查询操作.比如可以根据从高到低的顺序排序等.使用zadd对排序集合进行数据的添加.zadd sorted-set score1 member1 score2 member2... .支持两种集合间的操作,取交集和取并集.zinter, zunion.另外就是一些围绕score进行的操作.另外需要注意,默认的排名是从低到高的.比如有三个搜索引擎的排名:sousou : 2 baidu : 6 google : 10.排名是sousou 排第一(0), baidu排第二(1), google牌第三(2).可以通过zrank来查看排名.例如zrank zset google,也可以把排名反过来,即使用zrevrank zset google,这个时候google就排名第一(0)了.还可以查看排名表.使用zrange查看从上倒下的排名表,比如默认排名可以zrange zset 0 2,打印排名前三位的搜索引擎.反过来就是zrevrange zset 0 2.更多内容可以通过使用help @sorted-set获取.

3. key操作.

这里的这个key可不是hash里面那个key,而是面向整个redis数据库的.如果我这里用表来形容,想必是更加贴切.

keys pattern : 获取pattern指定的表名.pattern可以使用通配符*来进行匹配.如果想要获取所有表,使用keys *

rename old new : 更改key名.

expire key time : 指定超时时长,以秒为单位.

pexpire key time : 指定超时时长,以毫秒为单位.

persist key : 删除超时的表.我很纳闷的是,如果超时了这个表自己就会消失,为什么还需要这个命令呢.

ttl key : 获取以秒为单位的指定表的剩余时间.如果没有指定过超时,则剩余时间为-1

pttl key : 毫秒级单位的剩余时间,如果没有指定超时,则剩余时间为-1.

type key : 获取一个表的类型.即为上面提到的五种之一.

del key : 删除指定表.

dump key : 以序列化的方式显示指定表的内容.

更多内容可以通过help @generic来获取.

4. 事务

什么是事务呢,就是一组一起指定的命令.

multi : 如果要使用事务,首先输入multi表示事务的开始,然后就可以在后面输入各种命令了.

exec : 当各种命令都输入完成以后,执行exec命令,即可执行事务.

discard : 如果某个命令打错了.可以使用discard清空事务,从新输入.

watch : 事务的原子锁.如果需要对某个变量进行独占式访问,即可在执行multi之前watch 这个变量.

unwatch : 解锁

更多内容可以通过help @transactions

5. 发布/订阅

我可以说已经掌握了这个功能的用法,但是却还没有完全吃透这个功能应用的场景.只知道是用来通信的.

subscribe : 通过这个命令指定一个频道进行订阅.

psubscribe : 可以一次性指定多个频道进行订阅.

publish : 发布消息.首先指定之前用subscribe指定的频道作为第一个参数,然后输入想要发布的内容即可.

unsubscribe : 停止订阅.

punsubscribe : 也是停止订阅.支持pattern方式.

对于停止订阅不是很理解,因为订阅是前台的,如果不想订阅ctrl+c就行了.

更多内容可以使用help @pubsub来获取.

6. 连接

上面提到过一个命令ping,这个命令就是连接命令族的一个.用来查看是否已经启动服务.另外还有几个命令如下:

auth : 如果一个对redis使用了加密,那么就需要这个命令来输入密码.

select : redis默认有很多数据库,可以使用select来进行选择.

quit : 退出redis

echo : 回显内容.

没有更多内容了.如果有命令不确定,请输入help @connection获取.

7. 脚本

上面提到的事务虽然可以执行一系列的命令,但是并没有做到原子.脚本解决了这个问题.脚本中的一系列命令是原子执行的.即不会被打断.

redis的脚本使用的是lua脚本语法.

eval : 执行一个脚本.

evalsha : 执行一个加载后的脚本生成的sha1序列号.

script load : 加载一个脚本.

script flush : 清空脚本缓冲区.

script exists : 验证一个脚本是否存在

script kill : 杀死一个正在执行的脚本.不过注意到的一点是,因为脚本是原子的,所以只有在脚本没有执行过任何的些操作的前提下,才能够被杀死,否则就会执行完.

关于脚本需要举一个例子,然后说一下心得.

比如我谢了一个lua的脚本.setget.lua:

local string = redis.call("get", KEYS[1])

redis.call("set", KEYS[2], string)

无论我要加载这个命令还是执行这个命令,都得在命令行模式下使用:

1) 运行: 直接在命令行下执行 redis-cli eval "$(cat setget.lua)" 2 string string1

2) 加载: 直接在命令行下执行 redis-cli script load "$(cat setget.lua)" 然后记录下sha1序列号,到redis-cli下执行evalsha 序列号 2 string string1

很多人在写博客的时候并没有说清楚,如果我们去到redis-cli的模式下,上面的内容就变成了

eval "$(local string = redis.call('get', KEYS[1]) redis.call('set', KEYS[2], string)" 2 string string1

也就是说,redis并不识别cat,如果要在redis的模式先加载一个脚本,那么引号中的内容就是脚本的内容.

关于lua脚本还有一个需要补充的地方,lua脚本使用KEYS加下标的方式表示key, 下标从1开始.使用ARGS[]加下标的方式表示value, 下标同样从1开始.当执行一个lua脚本的时候,第一个参数永远是key的数量,哪怕key数量为0,也要指定.

关于脚本的更多内容,请使用help @scripting获取(其实也没啥能获取的了)

8. 服务器

这个主题的命令有很多.不一一介绍了(其实我也没有记住几个)

比较重要的是config命令:

config set  : 设置一些配置的值.比如密码就是通过这样设置的.config set requirepass "password"

config get  : 获取一些设置的值.可以通过config get *来获取全部的配置.如果登录成功了以后,可以通过config get requirepass 获取密码.

flushdb : 就在刚刚见识了它的效果,清空了我所有的当前数据库的表.

flushall : 如果没有体验过上面那个命令,这个命令更是你无法承受的重量.全部数据库的表都会被清空.

info : 获取服务器信息.

内容很多,不一一列举了.更多内容参见help @server

9. hiredis

到这里还有一个内容需要补充,远程登录redis的命令是redis-cli -h host -p port -a password

上面的第二个链接是hiredis的git链接.可以使用git clone获取hiredis源码.

下面说一下hiredis的简单用法.

首先是两个结构体:

redisContext : 这个结构体是操作redis的句柄.

redisReply : 保存redis命令执行以后的结果.

接下来是四个函数 :

redisContext * redisConnect(char * host, int port);

void * redisCommand(redisContext *, const char *format, ...);

void freeReplyObject(redisReply*);

void redisFree(redisContext*);

当然hiredis还有很多更高端的用法,我没有去了解,不过有了上面的认识,就可以进行简单的redis编程了.

简单的实现一个功能看看效果:

1) 插入一个string,内容是整型;

2) 插入一个string, 内容是字符串

3) 插入一个哈希表,内容既有整型又有字符串.

4) 一次查询这三张表.

代码实现如下:

/******************************************************************************
 ** Coypright(C) 2014-2024 () technology Co., Ltd
 **
 ** 文件名 : redistest.c
 ** 版本号 : 1.0
 ** 描  述 : 
 ** 作  者 : cp3alai
 ** 日  期 : 2015.07.01
 ******************************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <hiredis/hiredis.h>

#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6

int main(int argc, char **argv)
{
    redisContext *context;
    redisReply *reply;

    context = redisConnect("localhost", 6379);
    if (NULL == context)
    {
        return -1;
    }

    reply = (redisReply*)redisCommand(context, "set string 123");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("redis set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "set string1 string");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("redis set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "hmset hash alai good jj good");
    if (NULL == reply)
    {
        redisFree(context);
    }
    if (reply->type == REDIS_REPLY_STATUS)
    {
        printf("hash set result is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "strlen string");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_INTEGER)
    {
        printf("string's value is %lld\n", reply->integer);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "get string1");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_STRING)
    {
        printf("string1's value is %s\n", reply->str);
        freeReplyObject(reply);
    }

    reply = (redisReply*)redisCommand(context, "hgetall hash");
    if (NULL == reply)
    {
        redisFree(context);
        return -1;
    }
    if (reply->type == REDIS_REPLY_ARRAY)
    {
        int loop;
        redisReply *item;

        for (loop = 0; loop < reply->elements; loop++)
        {
            item = reply->element[loop];
            printf("hash %s is %s\n", loop % 2 == 0 ? "key" : "value", item->str);
        }
        freeReplyObject(reply);
    }

    redisFree(context);
    return 0;
}

这里需要注意的是,每一次执行redisCommand都会有malloc或者calloc等分配内存的动作发生,所以每次用完redisReply以后一定要记得释放空间,不然就会造成内存泄露.

我把每个循环中的释放空间的动作删除后,使用内存检测工具valgrind看一下效果:


ok.关于redis的内容就分享这么多.其实掌握这些才仅仅算是redis的入门,接下来需要面对的才是一些更重要的内容,比如备份,比如数据恢复,比如集群等.



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章