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; }
我把每个循环中的释放空间的动作删除后,使用内存检测工具valgrind看一下效果:
ok.关于redis的内容就分享这么多.其实掌握这些才仅仅算是redis的入门,接下来需要面对的才是一些更重要的内容,比如备份,比如数据恢复,比如集群等.