redis常用命令
Redis指令文档
连接控制
QUIT 关闭连接
AUTH (仅限启用时)简单的密码验证
适合全体类型的命令
EXISTS key 判断一个键是否存在;存在返回 1;否则返回0;
DEL key 删除某个key,或是一系列key;DEL key1 key2 key3 key4
TYPE key 返回某个key元素的数据类型 ( none:不存在,string:字符,list,set,zset,hash)
KEYS pattern 返回匹配的key列表 (KEYS foo*:查找foo开头的keys)
RANDOMKEY 随机获得一个已经存在的key,如果当前数据库为空,则返回空字符串
RENAME oldname newname更改key的名字,新键如果存在将被覆盖
RENAMENX oldname newname 更改key的名字,如果名字存在则更改失败
DBSIZE返回当前数据库的key的总数
EXPIRE设置某个key的过期时间(秒),(EXPIRE bruce 1000:设置bruce这个key1000秒后系统自动删除)注意:如果在还没有过期的时候,对值进行了改变,那么那个值会被清除。
TTL查找某个key还有多长时间过期,返回时间秒
SELECT index 选择数据库
MOVE key dbindex 将指定键从当前数据库移到目标数据库 dbindex。成功返回 1;否则返回0(源数据库不存在key或目标数据库已存在同名key);
FLUSHDB 清空当前数据库中的所有键
FLUSHALL 清空所有数据库中的所有键
处理字符串的命令
SET key value 给一个键设置字符串值。SET keyname datalength data (SET bruce 10 paitoubing:保存key为burce,字符串长度为10的一个字符串paitoubing到数据库),data最大不可超过1G。
GET key获取某个key 的value值。如key不存在,则返回字符串“nil”;如key的值不为字符串类型,则返回一个错误。
GETSET key value可以理解成获得的key的值然后SET这个值,更加方便的操作 (SET bruce 10 paitoubing,这个时候需要修改bruce变成1234567890并获取这个以前的数据paitoubing,GETSET bruce 10 1234567890)
MGET key1 key2 … keyN 一次性返回多个键的值
SETNX key value SETNX与SET的区别是SET可以创建与更新key的value,而SETNX是如果key不存在,则创建key与value数据
MSET key1 value1 key2 value2 … keyN valueN 在一次原子操作下一次性设置多个键和值
MSETNX key1 value1 key2 value2 … keyN valueN 在一次原子操作下一次性设置多个键和值(目标键不存在情况下,如果有一个以上的key已存在,则失败)
INCR key 自增键值
INCRBY key integer 令键值自增指定数值
DECR key 自减键值
DECRBY key integer 令键值自减指定数值
处理 lists 的命令
RPUSH key value 从 List 尾部添加一个元素(如序列不存在,则先创建,如已存在同名Key而非序列,则返回错误)
LPUSH key value 从 List 头部添加一个元素
LLEN key 返回一个 List 的长度
LRANGE key start end从自定的范围内返回序列的元素 (LRANGE testlist 0 2;返回序列testlist前0 1 2元素)
LTRIM key start end修剪某个范围之外的数据 (LTRIM testlist 0 2;保留0 1 2元素,其余的删除)
LINDEX key index返回某个位置的序列值(LINDEX testlist 0;返回序列testlist位置为0的元素)
LSET key index value更新某个位置元素的值
LREM key count value 从 List 的头部(count正数)或尾部(count负数)删除一定数量(count)匹配value的元素,返回删除的元素数量。
LPOP key 弹出 List 的第一个元素
RPOP key 弹出 List 的最后一个元素
RPOPLPUSH srckey dstkey 弹出 _srckey_ 中最后一个元素并将其压入 _dstkey_头部,key不存在或序列为空则返回“nil”
处理集合(sets)的命令(有索引无序序列)
SADD key member增加元素到SETS序列,如果元素(membe)不存在则添加成功 1,否则失败 0;(SADD testlist 3 \n one)
SREM key member 删除SETS序列的某个元素,如果元素不存在则失败0,否则成功 1(SREM testlist 3 \N one)
SPOP key 从集合中随机弹出一个成员
SMOVE srckey dstkey member 把一个SETS序列的某个元素 移动到 另外一个SETS序列 (SMOVE testlist test 3\n two;从序列testlist移动元素two到 test中,testlist中将不存在two元素)
SCARD key 统计某个SETS的序列的元素数量
SISMEMBER key member 获知指定成员是否存在于集合中
SINTER key1 key2 … keyN 返回 key1, key2, …, keyN 中的交集
SINTERSTORE dstkey key1 key2 … keyN 将 key1, key2, …, keyN 中的交集存入 dstkey
SUNION key1 key2 … keyN 返回 key1, key2, …, keyN 的并集
SUNIONSTORE dstkey key1 key2 … keyN 将 key1, key2, …, keyN 的并集存入 dstkey
SDIFF key1 key2 … keyN 依据 key2, …, keyN 求 key1 的差集。官方例子:
key1 = x,a,b,c
key2 = c
key3 = a,d
SDIFF key1,key2,key3 => x,b
SDIFFSTORE dstkey key1 key2 … keyN 依据 key2, …, keyN 求 key1 的差集并存入 dstkey
SMEMBERS key 返回某个序列的所有元素
SRANDMEMBER key 随机返回某个序列的元素
处理有序集合(sorted sets)的命令 (zsets)
ZADD key score member 添加指定成员到有序集合中,如果目标存在则更新score(分值,排序用)
ZREM key member 从有序集合删除指定成员
ZINCRBY key increment member 如果成员存在则将其增加_increment_,否则将设置一个score为_increment_的成员
ZRANGE key start end 返回升序排序后的指定范围的成员
ZREVRANGE key start end 返回降序排序后的指定范围的成员
ZRANGEBYSCORE key min max 返回所有符合score >= min和score <= max的成员 ZCARD key 返回有序集合的元素数量 ZSCORE key element 返回指定成员的SCORE值 ZREMRANGEBYSCORE key min max 删除符合 score >= min 和 score <= max 条件的所有成员
排序(List, Set, Sorted Set)
SORT key BY pattern LIMIT start end GET pattern ASC|DESC ALPHA 按照指定模式排序集合或List
SORT mylist
默认升序 ASC
SORT mylist DESC
SORT mylist LIMIT 0 10
从序号0开始,取10条
SORT mylist LIMIT 0 10 ALPHA DESC
按首字符排序
SORT mylist BY weight_*
SORT mylist BY weight_* GET object_*
SORT mylist BY weight_* GET object_* GET #
SORT mylist BY weight_* STORE resultkey
将返回的结果存放于resultkey序列(List)
持久控制
SAVE 同步保存数据到磁盘
BGSAVE 异步保存数据到磁盘
LASTSAVE 返回上次成功保存到磁盘的Unix时间戳
SHUTDOWN 同步保存到服务器并关闭 Redis 服务器(SAVE+QUIT)
BGREWRITEAOF 当日志文件过长时重写日志文件
远程控制命令
INFO 提供服务器的信息和统计信息
MONITOR 实时输出所有收到的请求
SLAVEOF 修改复制选项
redis目前提供四种数据类型:string,list,set及zset(sorted set)。
* string是最简单的类型,你可以理解成与Memcached一模一个的类型,一个key对应一个value,其上支持的操作与Memcached的操 作类似。但它的功能更丰富。
* list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。
* set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字。
* zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的 值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。
协议
redis目前只有基于TCP的文本协议,与memcache类似,有一些改进。
客户端通常发送
命令 参数… 值字节数\r\n
值\r\n
服务端的返回,根据第一个字节,可以判断:
- 错误信息
+ 普通文本信息
$ 变长字节数,$6表示CRLF之后有6个字节的字符
: 返回一个整数
* 返回组数,即*6表示CRLF之后将返回6组变长字符
注意事项:
Key不可包含空格或者回车符
Key不要过长或过短,应使其有意义,如”comment:1234:reply.to”
转载请注明来源:http://www.madcn.net/?p=693
redis 分析
1 简介
redis是一个类似memcached的key/value存储系统,它支持存储的value类型相对较多,包括string(字符串)、 list(链表)、set(集合)和zset(有序集合)。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数 据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master- slave(主从)同步。
2 分析
2.1 协议
本文是基于当前最新版本redis 1.2.6版本进行阅读分析的,支持的主要功能协议列表如下:
string | get/set/setnx/del/exists/incr/decr/mget |
list | rpush/lpush/rpop/lpop/llen/lindex/lset/lrange/ltrim/lrem/rpoplpush |
set | sadd/srem/smove/sismember/scard/spop/srandmember/
sinter/sinterstore/sunion/sunionstore/sdiff/sdiffstore/smembers |
zset | zadd/zincrby/zrem/zremrangebyscore/zrange/zrangebyscore/
zcount/zrevrange/zcard/zscore/incrby/descrby/ |
select/move/rename/renamenx/expire/expireat/sort/sync |
请求采用文本协议,整体分为两种:non-bulk(不带二进制数据)和multi-bulk(带二进制数据的)协议,具体协议格式分别如下:
non-bulk |
command argv … argv bulk_len\r\n |
bulk_data\r\n |
2.2 总体结构
程序运行的主流程如下图所示:
2.3 事件循环
redis网络实现没有采用开源的网络框架,具体的源文件为ae.h和ae.c。用aeEventLoop保存基于事
件系统的事件主循环,把event分为句柄事件(FileEvent)和超时事件(TimeEvent),用aeFiredEvent保存激活后的fd和event。相关的数据结构如下:
/* File event structure */
typedef struct aeFileEvent {
int mask; /* one of AE_(READABLE|WRITABLE|EXCEPTION) */
aeFileProc *rfileProc;
aeFileProc *wfileProc;
aeFileProc *efileProc;
void *clientData;
} aeFileEvent;
/* Time event structure */
typedef struct aeTimeEvent {
long long id; /* time event identifier. */
long when_sec; /* seconds */
long when_ms; /* milliseconds */
aeTimeProc *timeProc;
aeEventFinalizerProc *finalizerProc;
void *clientData;
struct aeTimeEvent *next;
} aeTimeEvent;
/* A fired event */
typedef struct aeFiredEvent {
int fd;
int mask;
} aeFiredEvent;
/* State of an event based program */
typedef struct aeEventLoop {
int maxfd;
long long timeEventNextId;
aeFileEvent events[AE_SETSIZE]; /* Registered events */
aeFiredEvent fired[AE_SETSIZE]; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
void *apidata; /* This is used for polling API specific data */
} aeEventLoop;
回调接口的定义如下:
// 句柄事件回调函数
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
// 超时回调函数
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
// 超时event删除回调函数
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
在网络相关操作中,定义了一组公共操作接口:aeApiCreate/ aeApiFree/aeApiAddEvent/aeApiDelEvent/aeApiPoll/aeApiName。在ae_epoll.c、 ae_kqueue.c和ae_select.c中,分别实现了基于epoll/kqueue和select系统调用的接口。在ae.c中会根据宏来 include对应的文件,系统调用的选择顺序依次为epoll -> kqueue -> select。
事件主循环实现在aeMain中,内部会调用aeProcessEvents函数。函数内部会先调用aeApiPoll处理fd上的注册事件,若 fd上有对应事件激活,调用对应的aeFileProc进行处理;然后调用processTimeEvents函数扫描超时链表处理已超时的超时事件,调 用对应的回调aeTimeProc处理。需要注意的是,aeEventLoop中采用单向链表实现了超时event链表,并且插入timeEvent时没 有保证按超时时刻有序,导致每次查找(aeSearchNearestTimer)离当前时间最近的timeEvent的复杂度为 O(N),aeDeleteTimeEvent删除timeEvent时的复杂度也为O(N), 删除timeEvent后需要从链表开始处重新查找已超时的timeEvent。在timeEvent链表长度较长时,操作效率会相对较低。
2.4 数据结构
2.4.1 sds (字符串)
redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。sdshdr
数据结构定义如下:
typedef char *sds;
struct sdshdr {
long len;
long free;
char buf[];
};
2.4.2 list(双向链表)
对list的定义和实现在源文件adlist.h/adlist.c,相关的数据结构定义如下:
// list迭代器
typedef struct listIter {
listNode *next;
int direction;
} listIter;
// list数据结构
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned int len;
listIter iter;
} list;
2.4.3 dict(hash表)
在源文件dict.h/dict.c中实现了hashtable的操作,数据结构的定义如下:
// dict中的元素项
typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
// dict相关配置函数
typedef struct dictType {
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
// dict定义
typedef struct dict {
dictEntry **table;
dictType *type;
unsigned long size;
unsigned long sizemask;
unsigned long used;
void *privdata;
} dict;
// dict迭代器
typedef struct dictIterator {
dict *ht;
int index;
dictEntry *entry, *nextEntry;
} dictIterator;
dict中table为dictEntry指针的数组,数组中每个成员为hash值相同元素的单向链表。set是在dict的基础上实现的,指定了key的比较函数为dictEncObjKeyCompare,若key相等则不再插入。
2.4.4 zset(排序set)
typedef struct zskiplistNode {
struct zskiplistNode **forward;
struct zskiplistNode *backward;
double score;
robj *obj;
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数
不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。如果把zsl中出现在不同层的同一个节点当作不同的节点,则整颗树上只有根节点包 含多个子节点,其他的子节点都至多有一个子节点。insert时Node的指针可能会插入到多条链中,实现时保证插入的每一个元素会出现在下标为0的左子 树上,因此可以通过该左子树遍历所有元素。
zsl的结构图如下所示:
插入节点时会随机生成level,level决定了节点会插入到哪几条路径上。由于level>=1,因此每个节点都会在下标为0的路径上存 在。可以看到,level越大的路径上节点数目越少。在查找>=score的节点时,利用这一特点,按照level递减的顺序从最大的level对 应的路径开始查找。找到该路径上小于score的最大节点x后,对level-1转换路径从x->forward[i]继续开始查找,直到 level最后减为0。返回x->forward[0],若不存在>=score的元素,则x->forward[0]为NULL。
zsl的插入过程如下图所示:
2.5 主从(master-slave)同步
启动时slave会向master发出指令SYNC进行全数据同步,slave会阻塞式等待数据同步完成加载到
内存中进行初始化后,才能开始请求处理。运行时数据同步是通过master转发请求给slave列表实现的,仅转发对数据有修改操作的请求。同步的状态机如下:
2.6 存储文件格式
redis使用了两种文件格式:全量数据和增量请求。全量数据格式是把内存中的数据写入磁盘,
便于下次读取文件进行加载;增量请求文件则是把内存中的数据序列化为操作请求,用于读取文件进行replay得到数据,序列化的操作包括SET、RPUSH、SADD、ZADD。
redis对interger根据值范围采用不同的编码存储,具体如下:
值范围 | 字节数(byte) | 编码格式 |
[0, 1 << 6 – 1] | 1 | 00 | value |
[1 << 6, 1 << 14 – 1] | 2 | 01 | (value >> | value & oxFF |
[1 << 14, 1 << 31 – 1] | 5 | 10 000000 | htonl(value) |
redis对数值类的string object编码存储格式如下:
值范围 | 字节数(byte) | 编码格式 |
[-(1 << 7), 1 << 7 – 1] | 2 | 1100 0000 | value & 0xFF |
[-(1 << 15), 1 << 15 – 1] | 3 | 1100 0001 |
(value >> & 0xFF | value & oxFF |
[-(1 << 31)], 1 << 31 – 1] | 5 | 1100 0010 |
value & oxFF | (value >> & 0xFF | (value >> 16) & 0xFF | (value >> 24) & 0xFF |
redis支持字符串压缩存储,压缩的编码格式如下:
1100 0011 | compl_len
(压缩后的长度) |
orig_len
(压缩前的长度) |
comp_value
(压缩后的内容) |
2.6.1 data文件格式
2.6.2 append文件格式
RedisDb | *2 | $6 | SELECT | $length(index) | index:long | ||||
entry | *3 | $3 | SET | $length(key) | key | $length(value) | value | ||
entry | *3 | $5 | RPUSH | $length(key) | key | $length(value) | value | ||
entry | *3 | $4 | SADD | $length(key) | key | $length(value) | value | ||
entry | *3 | $4 | ZADD | $length(key) | key | $length(score) | score:double | $length(value) | value |
entry | … | ||||||||
RedisDb | … | ||||||||
RedisDb | … | ||||||||
entry | |||||||||
entry | |||||||||
entry |
3 问题
3.1 内存泄露
在源文件hiredis.c中函数redisReadIntergerReply内部,会出现没有释放分配的内存块。
static redisReply *redisReadIntegerReply(int fd) {
// 在函数redisReadLine内部分配了sds需要的内存
sds buf = redisReadLine(fd);
redisReply *r = zmalloc(sizeof(*r));
// PROBLEM: buf为NULL时, 没有释放上面分配的redisRely指向的内存块
if (buf == NULL) return redisIOError();
r->type = REDIS_REPLY_INTEGER;
// PROBLEM: buf不为NULL时, 只是把buf的内容转化为interger, 没有释放buf指向的内存块
r->integer = strtoll(buf,NULL,10);
// FIX: 需要在这里手动调用sdsfree(buf)
return r;
}
3.2 同步机制
如2.5节所分析,redis实现的同步机制相对简单,缺少同步机制常见的check point和校验机制。
在运行时,如果master -> slave同步请求转发被丢弃, slave将无法恢复该请求的相关信息,直到slave重启时从master全量加载数据时才能修复。因此,建议使用redis尽量利用其 key/value和value支持多种类型的特性,存储一些相对不重要的数据。
原文:http://www.cnblogs.com/huli/archive/2010/06/06/1752778.html
Redis在Windows下的使用
Windows版的Redis可到此处下载,非官方版
http://code.google.com/p/servicestack/wiki/RedisWindowsDownload
Redis文件夹有以下几个文件
redis-server.exe:服务程序
指定redis的配置文件,如没有指定,则使用默认设置
D:\redis-2.0.0-rc2>redis-server.exe redis.conf
redis.conf配置选项如下
daemonize 是否以后台进程运行,默认为no
pidfile 如以后台进程运行,则需指定一个pid,默认为/var/run/redis.pid
bind 绑定主机IP,默认值为127.0.0.1(注释)
port 监听端口,默认为6379
timeout 超时时间,默认为300(秒)
loglevel 日志记录等级,有4个可选值,debug,verbose(默认值),notice,warning
logfile 日志记录方式,默认值为stdout
databases 可用数据库数,默认值为16,默认数据库为0
save <seconds> <changes> 指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。
save 900 1 900秒(15分钟)内至少有1个key被改变
save 300 10 300秒(5分钟)内至少有300个key被改变
save 60 10000 60秒内至少有10000个key被改变
rdbcompression 存储至本地数据库时是否压缩数据,默认为yes
dbfilename 本地数据库文件名,默认值为dump.rdb
dir 本地数据库存放路径,默认值为 ./
slaveof <masterip> <masterport> 当本机为从服务时,设置主服务的IP及端口(注释)
masterauth <master-password> 当本机为从服务时,设置主服务的连接密码(注释)
requirepass 连接密码(注释)
maxclients 最大客户端连接数,默认不限制(注释)
maxmemory <bytes> 设置最大内存,达到最大内存设置后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,任到达最大内存设置,将无法再进行写入操作。(注释)
appendonly 是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认值为no
appendfilename 更新日志文件名,默认值为appendonly.aof(注释)
appendfsync 更新日志条件,共有3个可选值。no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次(默认值)。
vm-enabled 是否使用虚拟内存,默认值为no
vm-swap-file 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-max-memory 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0。
Redis官方文档对VM的使用提出了一些建议: 当你的key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大. 当你的key不小时,可以考虑使用一些非常方法将很大的key变成很大的value,比如你可以考虑将key,value组合成一个新的value. 最好使用linux ext3 等对稀疏文件支持比较好的文件系统保存你的swap文件. vm-max-threads这个参数,可以设置访问swap文件的线程数,设置最好不要超过机器的核数.如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.
redis-cli.exe:命令行客户端,测试用
D:\redis-2.0.0-rc2>redis-cli.exe -h 127.0.0.1 -p 6379
设置一个Key并获取返回的值:
$ ./redis-cli set mykey somevalue
OK
$ ./redis-cli get mykey
Somevalue
如何添加值到list:
$ ./redis-cli lpush mylist firstvalue
OK
$ ./redis-cli lpush mylist secondvalue
OK
$ ./redis-cli lpush mylist thirdvalue
OK
$ ./redis-cli lrange mylist 0 -1
1. thirdvalue
2. secondvalue
3. firstvalue
$ ./redis-cli rpop mylist
firstvalue
$ ./redis-cli lrange mylist 0 -1
1. thirdvalue
2. secondvalue
redis-check-dump.exe:本地数据库检查
redis-check-aof.exe:更新日志检查
redis-benchmark.exe:性能测试,用以模拟同时由N个客户端发送M个 SETs/GETs 查询 (类似于 Apache 的 ab 工具).
./redis-benchmark -n 100000 –c 50
====== SET ======
100007 requests completed in 0.88 seconds (译者注:100004 查询完成于 1.14 秒 )
50 parallel clients (译者注:50个并发客户端)
3 bytes payload (译者注:3字节有效载荷)
keep alive: 1 (译者注:保持1个连接)
58.50% <= 0 milliseconds(译者注:毫秒)
99.17% <= 1 milliseconds
99.58% <= 2 milliseconds
99.85% <= 3 milliseconds
99.90% <= 6 milliseconds
100.00% <= 9 milliseconds
114293.71 requests per second(译者注:每秒 114293.71 次查询)