Redis 服务器
Remote Dictionay Server
Redis是一个key-value持久化产品,通常被称为数据结构服务器。
Redis的key是string类型;value可以是string、hash、list、set、sorted set等类型;
实际上,Redis内部会将key和value都以二进制字节流的格式存储。
安装 redis:
redis官网:
http://code.google.com/p/redis/
安装方法:
http://code.google.com/p/redis/wiki/QuickStart
$ tar xvzf redis-1.02.tar.gz
$ cd redis-1.02
$ make
http://code.google.com/p/redis/wiki/QuickStart
$ tar xvzf redis-1.02.tar.gz
$ cd redis-1.02
$ make
$ make test
部署:
将redis-cli redis-benchmark redis-server redis.conf靠到指定的目录。
修改配置文件:
在redis.conf可以指定db的保存路径,默认是保存在当前目录。具体内容见redis.conf中的注释。
启动:
./redis-server
部署:
将redis-cli redis-benchmark redis-server redis.conf靠到指定的目录。
修改配置文件:
在redis.conf可以指定db的保存路径,默认是保存在当前目录。具体内容见redis.conf中的注释。
启动:
./redis-server
KEY操作
exists key 测试key是否存在,返回1存在,0不存在
dbsize 当前数据库的key数量
dump key 返回被序列化的值
del key1 key2 ... 删除给定的key,返回删除key的数目
type key 返回给定key的value类型,返回none表示不存在key
keys patten 返回匹配指定模式的所有key
randomkey 返回一个随机选择的key
rename oldkey newkey 重命名一个key,如果newkey存在将会被覆盖
renamenx oldkey newkey 重命名一个key,如果newkey存在返回失败
expire key seconds 为key指定过期时间,相对时间
expireat key timestamp 为key指定过期时间,绝对时间(UNIX时间)
ttl key 返回设置过期时间的key的剩余过期秒数
persist key 移除key的过期时间
VALUE的五种数据结构
1、string(字符串)
set key value 插入一条数据(key-value pair),若key已存在,则覆盖旧value
append key value 对已存在的key,在其value末尾添加新数据;如key不存在,等效于set命令
setex key seconds value 原子性完成两个操作,一是设置key值为指定value,同时设置该key的ttl
setnx key val key不存在时才插入该记录
get key 按键索引数据,如key不存在,返回nil
getset key value 将key设置为新的value,并返回旧value
mget key1 key2 返回多个key的值
mset key1 val1 key2 val2
getrange key start end 返回value的一个切片,双闭区间
setrange key offset value 替换key的部分字符串值
substr key start end
getbit
setbit
strlen key value长度
incr key value++(原子操作),将value当成整型处理
incrby key n value+=n
decr key value--
decrby key n value-=n
2、hash(哈希表)
哈希表结构
hset table field value
hmset table field1 val1 field2 val2 ...
hget table field
hmget table field1 field2 field3 ...
hdel table field
hgetall table
hkeys table
hvals table
3、list(列表)
FIFO结构,用链表实现,支持快速插入元素,但查找性能比较低
lpush list node 在list头部插入
rpush list node 在list尾部插入
lpop list 在list头部删除
rpop list 在list尾部删除
linsert list before val_b node 在val_b前插入新元素val
lrange list start end 返回list的一个切片 list[start:end],双闭区间,索引可以为负数,-1表示最后一个元素,-2表示倒数第二个元素
ltrim list start end 对list进行裁减,只保留指定范围内的元素
llen list list长度
4、set(集合)
将一系列不重复的值存储成一个集合
sadd set element 往集合中添加元素
srem set element 从集合中删除指定元素
smembers set 返回集合所有元素
sinter set1 set2 取交集
sunion set1 set2 取并集
sdiff set1 set2 取差集
5、sorted set(有序集合)
与set类似,但sorted set中的数据有一个score属性,集合中的所有元素按照其score排序。
sorted set采用了skip list结构的实现,插入一个元素的时间复杂度是O(logN)
zadd sorted_set score member 添加元素,并指定score
zscore sorted_set member 返回指定元素的score
zrange sorted_set start end [withscores] 返回从start到end次序的元素,zrange sorted_set 0 -1 返回所有member
zrevrange sorted_set start end 反序,按score从大到小的顺序返回
zrangebyscore sorted_set min_score max_score 返回score在指定范围内的元素
zcard sorted_set 返回集合中的元素个数
zcount sorted_set min max 返回集合中在指定分数范围内的元素个数
zremrangebyscore sorted_set min_score max_score 删除score在指定范围内的元素
zrank sorted_set member 返回元素在集合中的排名(从0开始计算)
zincrby sorted_set increment member 为指定元素的score加上一个增量increment
zrem sorted_set member 删除指定元素
6、publish/subscribe(订阅)
可以将数据推送到某个信息管道中,然后其他人可以通过订阅这些管道来获取推送过来的信息。
publish channlone key
subscribe channlone
常用命令:
info 打印redis信息
select n 选取数据库(默认支持16个db)
move key n 将key从当前库移至指定数据库
flushdb 清空当前数据库
flushall 清空所有数据库
config get parameter 读取服务器的运行时参数(redis.conf文件中的配置项)
config set parameter value 重新配置运行时参数
save 保存数据快照(dump.rdb)
bgsave
bgrewriteaof 压缩aof持久化文件
shutdown 停止所有客户端,同时以阻塞的方式执行内存数据持久化
slaveof host port 修改SLAVE服务器的复制设置
管线(pipelining)
客户端在发送命令之后,不用立即等待来自服务器的响应,而是可以继续发送后面的命令,
在命令发送完毕之后,再一次性的读取之前所有命令的应答。
持久化(persistence)
Redis 的数据持久化是通过将内存中的数据同步到磁盘来实现的,Redis支持两种持久化方式:
1、Snapshotting,将内存中的数据以快照方式写入二进制文件中,默认文件名为dump.rdb。
相关配置:
save seconds changed # 在指定时间内,如果超过changed个key被修改,则发起快照保存
快照保存过程:
- fork一个子进程,父进程继续处理client请求,子进程负责将内存内容写入临时文件;
- 由于os的写时复制技术(copy on write),当父进程处理写请求时,os会为其要修改的页面创建副本,所以子进程的地址空间数据是fork时刻整个数据库的一个快照;
- 子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
使用snap shot方式,当Redis物理内存使用超过内存总量的3/5时就会开始有crash的风险。
fork调用的copy-on-write机制是基于操作系统页这个单位的,也就是只有写入的脏页会被复制,通常系统不会在短时间内所有页都发生了写入而导致复制。
crash的真正原因是持久化使用了Buffer IO,所谓Buffer IO是指Redis对持久化文件的写入和读取操作都会使用物理内存的Page Cache。
2、Append-only file(aof),快照方式实在一定间隔时间做一次,如果redis发送了意外crash,就会丢失最后一次快照后的所有修改。
aof方式会将每一个收到的写命令通过write函数追加到文件中(默认是appendonly.aof),当redis重启时通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
相关配置:
appendonly yes # 启用aof持久化方式;
appendfsync always # 每次收到写命令就立即强制写入磁盘,最慢,但是保证完全的持久化;
appendfsync everysec # 每秒强制写入磁盘一次
appendfsync no # 依赖os内核自身的缓存机制
redis-check-aof --fix <filename> # 修复损坏的AOF文件
aof方式类似mysql的基于语句的binlog方式,可能会导致log文件体积过大,当系统重启恢复数据时如果是aof方式则加载数据会非常慢。
事务(transactions)
事务特征:
1、在事务中的所有命令都会被串行化的顺序执行,事务执行期间,Redis不会再为client的请求提供任何服务,从而保证事务中的所有命令被原子的执行;
2、Redis事务中如果有一条命令执行失败,其后的命令仍然会继续执行;
3、当使用aof模式时,Redis会将事务内的所有写操作在本次调用中全部写入磁盘,如果在写入的过程中出现崩溃(如断电),那么此时也许只有部分数据被写入磁盘,Redis服务器在重新启动时执行一系列必要的一致性检测,如果发现问题,可以将已写入的部分数据进行回滚。
事务的提交和回滚操作:
1、multi...exec的命令组合类似于关系数据库的BEGIN TRANSACTION ... COMMIT语句;
watch key1, key2...
multi
cmd1
cmd2
...
exec
2、multi...discard的命令组合类似于关系数据库的BEGIN TRANSACTION ... ROLLBACK语句;
multi
cmd1
cmd2
...
discard
在multi命令执行之前,可以使用watch命令指定待监控的keys;
然后在执行EXEC之前,如果被监控的keys发生了修改(被其它客户端更改了),EXEC将放弃执行该事务队列中的所有命令。
在事务中,可以使用unwatch命令取消当前事务监控的keys;
如果已经执行了EXEC或DISCARD命令,则无需再手工执行unwatch命令,因为在此之后,事务中所有被监控的keys都将自动取消。
主从复制(replication)
Replication的特征:
1、同一个Master可以同步多个Slaves;
2、Master Server以非阻塞的方式为Slaves提供服务,因此,在主从同步期间,client仍可以提交查询或修改请求;
3、
Replication的工作原理:
在Slave启动并连接到Master之后,它将主动发送一个SYNC命令,此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,
在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载内存中。
此后, Master继续将所有已收集到的修改命令,和新的修改命令一次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
虚拟内存( virtual memory)
Redis的虚拟内存是为了保证key的查找速度,只会将value交换到swap文件中。
因此,如果Redis的内存问题是由于太多value很小的key造成的,那么虚拟内存并不解决问题。
Redis没有使用操作系统提供的虚拟内存机制,而是实现了自己的虚拟内存机制,主要是因为以下两点:
1、操作系统的虚拟内存是以4K页面为最小单位进行的,而redis的大多数对象都远小于4K;
2、Redis可以将交换到磁盘的对象进行压缩,一般压缩后的对象回比内存中的对象小10倍;
相关配置:
vm-enabled yes # 打开虚拟内存功能
vm-max-memory bytes # Redis使用的内存上限
vm-swap-file path # 交换出来的value保存的文件路径
vm-page-size bytes # 页面大小
vm-pages number # 页面数量的上限
vm-max-threads num # 执行value换入换出的工作线程数量
一个value可以保持在多个page里面,但一个page只能保存一个value;
在Redis使用的内存没超过vm-max-memory之前是不会交换任何value的。
当超过最大内存限制后,Redis优先选择过期或较大的对象进行交换:
swappablility = age * log(size_in_memory)
vm-max-threads=0时,会启用阻塞式虚拟内存,以同步方式执行I/O操作。
删除过期数据
1、惰性删除,当客户端访问一个key的时候,会顺带检查其是否过期,从而保证用户不会访问到过期的key;
2、定期删除,每隔一段时间,对expires字典进行随机检查,并删除过期的key;
3、强制删除,当redis使用的内存空间超过maxmemory的配置值时,默认采用lru算法淘汰key,被删除的key可以是未过期的,更多淘汰算法如下:
redis 中的默认的过期策略是volatile-lru 。设置方式
config set maxmemory-policy volatile-lru
maxmemory-policy 六种方式
1.volatile-lru,从设置了过期时间的数据集中,选择最近最久未使用的数据释放;
2.allkeys-lru,从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;
3.volatile-random,从设置了过期时间的数据集中,随机选择一个数据进行释放;
4.allkeys-random,从数据集中(包括了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放;
5.volatile-ttl,从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;
6.noeviction,不删除任意数据(但redis还会根据引用计数器进行释放呦~),这时如果内存不够时,会直接返回错误;
注意:
在Redis中LRU算法是一个近似算法,默认情况下,Redis随机挑选5个键,并且从中选取一个最近最久未使用的key进行淘汰,在配置文件中可以通过maxmemory-samples的值来设置redis需要检查key的个数,但是栓查的越多,耗费的时间也就越久,但是结构越精确(也就是Redis从内存中淘汰的对象未使用的时间也就越久),设置多少,综合权衡吧。
惰性删除的CPU开销很小,但如果key没有被访问到,即使其过期了,也不会被删除,它无法及时释放过期key占用的内存;
定期删除的CPU开销比较大,但能及时释放过期key占用的内存,可以通过限制删除操作执行的时长和频率,来减少删除操作对 CPU 时间的影响;
通信协议(Protocol)
*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF
注意:命令本身也作为协议的一个参数来传送;
比如:client要向server发送如下命令:set mykey myvalue
则格式化成协议的数据为:
"*3
$3
SET
$5
mykey
$7
myvalue
"
Redis命令会返回不同类型的回复,每种类型的回复以第一个字节区分:
1、status reply: "+",例如:"+OK
"
2、error reply: "-",例如:"-ERR unknown command 'foobar'"
3、interger reply: ":",例如:":1000
"
4、bulk reply: "$",例如:"$6
foobar
"
5、multi bulk reply: "*",例如:"*4
$3
foo
$3
bar
$5
Hello
$5
World
"
status reply通常是由那些不需要返回数据的命令返回(如set命令),这种回复不是binary safe的;
bulk reply用来返回binary safe字符串,字符串的最大长度为512M,常用于get命令的返回结果;
如果请求的key不存在,会返回"$-1
",这称为NULL Bulk Reply;
multi bulk reply常用于lrange这样的命令,它返回多个值。
Empty Multi Bulk Reply: "*0
"
NULL Multi Bulk Reply: "*-1
"
C 客户端官
官网:
http://github.com/redis/hiredis
安装方法:
git clone https://github.com/redis/hiredis
$ cd hiredis
$ make
$ make install
编译:
以hiredis自带的example.c程序为例:
gcc -lhiredis example.c