Starter
安装
安装
> wget http://download.redis.io/releases/redis-3.0.2.tar.gz > tar zxvf redis-3.0.2.tar.gz > cd redis-3.0.2 > make # 安装完之后,会在src目录下生成几个可执行文件 > make test # 检查make有没有问题 > make install # 会自动将src下生成的新的可执行文件copy到/usr/local/bin下
配置
#将默认配置文件redis.conf 拷贝到固定目录下(/etc/redis) > mkdir /etc/redis > cp redis.conf /etc/redis > vi /etc/redis/redis.conf # 可修改配置 daemonize yes (表示后台运行)
启动
# Server > redis-server /etc/redis/redis.conf
# Client # redis-cli [OPTIONS] [cmd [arg [arg ...]]] # -h <主机ip>,默认是127.0.0.1 # -p <端口>,默认是6379 # -a <密码>,如果redis加锁,需要传递密码 # --help,显示帮助信息 > redis-cli
关闭
# Close Client > redis-cli shutdown
# Close Server > pkill redis-server
> netstat -tunpl | grep redis > kill pid
注意:make失败,错误信息如下
cc: error: ../deps/hiredis/libhiredis.a: No such file or directory
cc: error: ../deps/lua/src/liblua.a: No such file or directory
cc: error: ../deps/jemalloc/lib/libjemalloc.a: No such file or directory
make: *** [redis-server] Error 1
解决方案:分别进入redis下的deps下的hiredis、lua和jemalloc下运行make
> cd deps/hiredis
> make
> yum install -y readline-devel pcre-devel
> cd deps/lua
> make
> yum install openssl-devel
> cd deps/jemalloc
> ./configure
> make
安全性
客户端执行命令前先通过密码认证 note: Redis速度很快,一般一个外部用户可以在1s进行150k次的密码尝试
Server端配置
> vi /etc/redis/redis.conf requirepass <password>
Client端连接
> redis-cli -a <password>
或者
> redis-cli > auth <password>
事务处理
Redis对事务的支持比较简单,只能保证一个Client发起的事务中的命令连续执行
muti
: 开始事务- 进入一个事务上下文
- 后续命令会放入一个队列(不立即执行)
discard
: 取消事务(回滚)exec
: 执行事务 (提交)- 顺序执行队列中的所有命令
- 注意: 队列中某个命令执行错误,整个事务并不会回滚,而是将可执行的命令都提交了!
> get age -> 22
> muti -> OK
> set age 10 -> QUEUED
> set age 20 -> QUEUED
> exec -> OK OK
> get age -> 20
使用乐观锁进行复杂事务控制:大多数基于数据版本(version)的记录机制实现
watch xxx
:监控某key (乐观锁)exec
时发现watch的key发生过变化,则整个事务失败exec
,discard
,unwatch
:会清除连接中的所有监视
持久化机制
Redis 是一个支持持久化的内存数据库 (可将内存中数据同步到硬盘来保证持久化) 两种方式:
snapshotting
快照方式 (默认)- 存储数据,一定时间间隔做一次
- 默认存储文件:
/redis/bin/dump.rdb
- 可配置自动做快照持久化的方式,eg:
save 900 10 #900s内如果超过10个key被修改,就发起快照保存
- 默认存储文件:
- 存储数据,一定时间间隔做一次
append-only file
方式 (缩写:aof)- 存储操作
- 默认存储文件:
/redis/bin/appendonly.aof
- 可配置通过fsync函数强制os写入磁盘的时机,eg:
> vi /etc/redis/redis.conf appendonly yes //启用aof持久化方式 #appendfsync always //收到命令立即写入磁盘,最慢,但保证完成持久化 appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化之间做了折中 #appendfsync no //完成依赖os,性能最好,持久化没有保证
虚拟内存
提高数据库容量的方法:
- 将数据分割到多个redis server
- 利用虚拟内存,把不经常访问的数据从内存交换到磁盘中,腾出内存空间用于其他需要访问的数据
虚拟内存配置:
> vi /etc/redis/redis.conf
vm-enabled yes # 开启vm功能
really-use-vm yes
vm-swap-file /tmp/redis.swap # 制定保存交换出的value的文件
vm-max-memory 100000 # Redis使用的最大内存上限
vm-page-size 32 # 每个页面大小 32字节
vm-pages 134217728 # 最多使用多少页
vm-max-threads 4 # 执行value对象换入的线程数量
主从复制
- 主 Master server
- 从 Slave servers
- Master 可以拥有多个Slave,Slave可以连接Slave,主从复制不会阻塞Master
主从Server可以在:不同机器 or 同机器不同端口
Slave Server端配置
> vi /etc/redis/redis.conf slaveof <master-ip> <master-port> # 如果master设置了验证密码,需配置masterauth masterauth <master-password>
Start Master & Slave Servers
> redis-server /etc/redis/redis.conf
Client连接测试
> redis-cli -h xxxx -p xxxx > info # 查看状态
Master Server info sample:
# Replication role:master connected slaves:1 slave0:ip=192.168.0.110,prot=6379,status:online,offset=00,lag=0 master_rep1_offset:99 rep1_backlog_active:1 ...
Slave Server info sample:
#Replication role:slave master_host:192.168.0.100 master_port:6379 master_link_status:up ...
注意: redis主从复制时提示master_link_status:down
- 确定slave的配置文件
redis.conf
配置正确(slaveof
和masterauth
参数) Unable to AUTH to MASTER: Writing to master: No route to host
=> 把master和slave防火墙关掉service iptables stop
,之后重启No route to host
=>iptables -F
发布订阅消息
Redis在订阅者和发布者之间起到消息路由的功能
- 订阅者使用
subscribe
,psubscribe
命令向redis server订阅消息subscribe <channel...>
:订阅者监听频道
- 发布者使用
publish
命令想redis server发送特定类型的消息publish <channel> <content>
:在某频道发布信息
示例:
Client 1:
> subscribe tv1
Client 2:
> subscribe tv1 tv2
Client 3:
> publish tv1 Hello # return 2 代表有两个客户端正在监听tv1
> publish tv2 World # return 1
Result:
- Client 1 收到 message tv1 Hello
- Client 2 收到 message tv1 Hello message tv2 World
常用命令
键值相关命令
keys
: 返回给定pattern的所有key> keys * > keys my*
exists
: 确认一个key是否存在=>return 1(存在)/0(不存在)> exists name > exists age
del
: 删除一个key => return 1(success)/0(fail)> del list2 > exists list2
expire
: 设置某个key的过期时间(秒)> expire addr 10 > ttl addr #查看key还有多少时间过期(有效时长),返回-1表示已过期或取消了过期时间 > get addr
move
: 转移key到另一个数据库> select 0 # 选择数据库0 > move age 1 # 将数据库0中age移动到数据库1中
persist
: 移除key的过期时间> persist age # return 1 > ttl age # return -1 > get age
randomkey
: 随机返回数据库中一个key> randomkey > randomkey
rename
: 重命名key> rename age age_new # return OK > keys age*
type
: 返回key类型> type myset # return set > type list1 # return list > type kk # return nil
服务器相关命令
select
: 选择数据库( Redis数据库编号从0~15)> select 1 # return OK > select 5 # return OK > select 16 # return (error) ERR invalid DB index
quit
: 退出连接> quit
dbsize
: 返回当前数据库中key的数量> dbsize
info
: 获取服务器的统计信息> info redis_version:3.0.1 redis_git_sha1:xxx redis_git_dirty:0 arch_bits:32 multiplexing_api:epoll process_id:xxx uptime_in_seconds:xxx uptime_in_days:0 .... db0:keys=xxx,expire=xxx db1:xxx ...
config get
: 实时存储收到的请求 (获取一些相关配置的参数)> config get * # 获取所有配置参数信息 > config get timeout
flushdb
: 删除当前数据库中所有key(清空当前数据库)> dbsize > flushdb > dbsize
flushall
:删除所有数据库的所有key(清空所有数据库)> flushall
数据类型
String
- 二进制安全
- 一个key对应一个value
value可包含任何数据(eg:jpg,序列化对象)
set
> set name Tom # return "OK" > get name # return "Tom"
setnx
:set not exist => return 0(fail) / 1(success)> setnx name Jack # return 0 > get name # return "Tom"
setex
:set expireTime> setex color 10 red # return "OK" > get color # return "red" # 10s later... > get color # return (nil)
setrange
:replace string by range => return new string length> get email # return "Tom@gmail.com" > setrange email 4 163.com # return 13 > get email # return "Tom@163.comom"
mset
:muti-set => return "OK" / 0 (one fail => all fail)msetnx: muti-set not exist # return "OK" / 0 (one fail -> all fail) > mset key1 a1 key2 a2 key3 a3 # return "OK" > get key2 # return "a2"
getset
:设置key的值,并返回key的旧值> get name # return "Tom" > getset name Jack # return "Tom" > get name # return "Jack"
getrange
:get substring> get email # return "Tom@163.comom" > getrange email 0 5 # return "Tom@16"
mget
: muti-get> mget key1 key2 key3 key4 # return "a1" "a2" "a3" (nil)
append
:追加value => return new string length> get name # return "Jack" > append name .net # 8 > get name # return "Jack.net"
strlen
: return value length> strlen name # return 8
incr
: i++;decr
: i--> set age 20 # return "20" > incr age # return "21" > incr age # return "22"
> get cc # return (nil) > incr # return 1 > incr # return 2
incrby
: i+n;decrby
: i-n> incr bb 5 # return 5 > incr bb 5 # return 10 > incr bb -2 # return 8
hash
hset hash field fieldValue
> hset user:001 name Tom # return 1 > hget user:001 name # return "Tom"
hsetnx
> hsetnx user:001 name Jack # return 0 > hget user:001 name # return "Tom"
hmset
> hmset user:001 name Lucy age 20 # return "OK" > hget user:001 age # return "20"
hmget
> hmget user:001 name age # return "Lucy" "20"
hincrby
> hincrby user:001 age 5 # return "25"
hexists
: test field exist or not => return 1(exist)/0 (not exist)> hexists user:001 age # return 1 > hexists user:001 sex # return 0
hlen
: field length> hlen user:001 # return 2
hkeys
: return all fields> hkeys user:001 # return "name" "age"
hvals
: return all values> hvals user:001 # return "Lucy" "20"
hgetall
: return all fields and values> hgetall user:001 # return "name" "Lucy" "age" "20"
hdel
: del hash field> hdel user:001 age # return 1 > hget user:001 age # return (nil)
list
每个子元素都是String类型的双向链表,即可做栈又可做队列 可以通过push,pop操作从头部或尾部添加删除元素
lpush
,lpop
,lrang
lpush
从头部压入一个元素 => return list sizelpop
从头部删除一个元素 => return 弹出的元素lrange
从头开始取元素( 0表示头部第一个元素,-1表示尾部第一个元素)> lpush list1 "Hello" # return 1 > lpush list1 "World" # return 2 > lrange list1 0 -1 # return "World" "Hello" (从头取到尾) # 类似栈(先进后出) > lpop list1 # return "World" > lrange list1 # return "Hello"
rpush
,rpop
rpush
从尾部压入一个元素rpop
从尾部弹出一个元素> rpush list2 "Hello" > rpush list2 "World" > lrange list2 0 -1 # return "Hello" "World" # 类似队列(先进先出) > rpop list2 # return "World" > lrange list2 # return "Hello"
linsert
,lset
linsert
在特定位置添加元素lset
替换指定下标的元素> lpush list3 "one" # return 1 > lpush list3 "two" # return 2 > lrange list3 0 -1 # return "two" "one" > linsert list3 before "one" "three" # return 3 > lrange list3 0 -1 # return "two" "three" "one" > lset list3 1 "four" # return OK > lrange list3 0 -1 # return "two" "four" "one"
lrem
删除n个值为value的元素n=0
表示全部删除n>0
从头部开始删除n<0
从尾部开始删除- return 删除个数
> lpush list3 "one" > lpush list3 "one" > lpush list3 "one" > lrange list3 0 -1 # return "one" "one" "one" "two" "four" "one" > lrem list3 3 "one" # return 3 > lrange list3 0 -1 # return "two" "four" "one" > lrem list3 1 "one" # return 1 > lrange list3 0 -1 # return "two" "four" > lrem list3 1 "one" # return 0
ltrim
保留指定范围的元素,其他全部删除 => return OK> rpush list4 "a1" > rpush list4 "a2" > rpush list4 "a3" > rpush list4 "a4" > lrange list4 0 -1 # return "a1" "a2" "a3" "a4" > ltrim list4 1 2 # return OK > lrange list4 0 -1 # return "a2" "a3"
rpoplpush list1 list2
=> list1从尾部弹出元素,从头压入list2,return 弹出元素# list5: a1 a2 a3 # list6: b1 b2 b3 > rpoplpush list5 list6 # a1 > lrange list5 0 -1 # a1 a2 > lrange list6 0 -1 # a3 b1 b2 b3
lindex list index
=> return list中index位置的元素> lindex list6 3 # return b3
llen list
-> return list size> llen list6 # return 4
set
string类型的无序集合,通过hash table实现 集合可取并集,交集,差集等操作 (可用于实现类似好友推荐,blog的tag功能)
sadd
,srem
,smembers
sadd
集合中添加元素 => return 1(success)/0(fail)srem
集合中删除元素 => return 1(success)/0(fail)smembers
列出集合所有元素> sadd myset "Hello" # return 1 > sadd myset "World" # return 1 > sadd myset "World" # return 0 > smembers myset # return "World" "Hello" > srem myset "Hello" # return 1 > srem myset "Hello" # return 0 > smembers myset # return "World"
spop
,srandmembber
spop
从集合中随机弹出一个元素 => return 弹出的元素srandmember
随机返回集合的一个元素,但不删除元素# 集合mytest1:"a1","a2","a3","a4","a5" > spop mytest1 # return "a3" (随机的一个元素) > smembers mytest1 # return "a1" "a2" "a4" "a5" > srandmember mytest1 # return "a2" (随机的一个元素) > smembers mytest1 # return "a1" "a2" "a4" "a5"
sdiff
,sdiffstore
sdiff
返回两个集合的差集,以前一个集合为标准sdiffstore
将返回的差集存储到另一个集合 -> return 1(success)/0(fail)# test1: a1,a2,a3 # test2: b1,a2,b3 > sdiff test1 test2 # return "a1" "a3" > sdiff test2 test1 # return "b1" "b3" > sdiffstore mytest3 test1 test2 # return 1 > smembers mytest3 # return "a1" "a3"
sinter
,sunion
sinter
返回两个集合的交集,并存储到另一个集合 =>return 1(success)/0(fail)sunion
返回两个集合的并集,并存储到另一个集合 => return 元素个数# test1: a1,a2,a3 # test2: b1,a2,b3 > sinter test1 test2 # return "a2" > sinter mytset4 test1 test2 # return 1 > smembers mytest4 # return "a2" > sunion test1 test2 # return a1 a2 a3 b1 b3 > sunionstore mytest5 test1 test2 # return 5 > smembers mytest5 # return a1 a2 a3 b1 b3
smove
将一个元素从集合1移到集合2 => return 1(success)/0(fail)> smove test1 test2 a1 > smembers test1 # return a2 a3 > smembers test2 # return a1 b1 a2 b3
scard
返回集合元素个数> scard test1 # return 2 > scard test2 # return 4
sismember
测试某个元素是否在某个集合中=> return 1(yes)/0(no)> sismember test1 a2 # return 1 > sismember test1 a1 # return 0
zset
- sorted set类型,set的升级,有序集合
- 每次添加修改元素后会自动重新排序
- 每一个元素有两个值,值value和分数score(专门用来做排序)
- eg:
n1 -> {(value1,score1),(value2,score2),...}
- rank: index
- lexicographical: 相同的分值时,有序集合的元素会根据成员的值逐字节对比
rank | value | score |
---|---|---|
0 | one | 1 |
1 | two | 2 |
2 | three | 3 |
zadd
向有序集合添加一个元素并指定顺序 => return 1(success)/0(添加失败,但会更新顺序号)# zadd name score1 value1 score2 value2 ... redis:6379> zadd cat 10 cat-A 20 cat-B 30 cat-C 40 cat-D (integer) 4
zscan key cursor
查看集合元素们# zscan name cursor ... redis:6379> zscan cat 0 1) "0" 2) 1) "cat-A" 2) "10" 3) "cat-B" 4) "20" 5) "cat-C" 6) "30" 7) "cat-D" 8) "40"
zrange/zrevrange
获取rank范围内的元素;zrangebyscore
获取score范围内的元素# zrange name start end [withscores] redis:6379> zrange cat 1 3 1) "cat-B" 2) "cat-C" 3) "cat-D" redis:6379> zrange cat 1 3 withscores 1) "cat-B" 2) "20" 3) "cat-C" 4) "30" 5) "cat-D" 6) "40" # zrangebyscore name minscore maxscore [withscores] redis:6379> zrangebyscore cat 15 45 1) "cat-B" 2) "cat-C" 3) "cat-D"
zrank/zrevrank
获取元素rank;zscore
获取元素score# zrank name value redis:6379> zrank cat cat-C (integer) 2 redis:6379> zrevrank cat cat-C (integer) 1 # zscore name value redis:6379> zscore cat cat-B "20"
zcard
返回集合中所有元素个数;zcount
返回集合指定顺序号范围内的元素数量# zcard key redis:6379> zcard cat (integer) 4 # zcount name minscore maxscore redis:6379> zcount cat 15 45 (integer) 3
zinterstore
交集,zunionstore
并集redis:6379> zadd cat2 15 cat-B 45 cat-D 55 cat-E (integer) 3 # zinterstore name destName numKeys key1 key2 ... [aggregate sum|max|min] redis:6379> zinterstore cat-inter 2 cat cat2 (integer) 2 redis:6379> zscan cat-inter 0 1) "0" 2) 1) "cat-B" 2) "35" 3) "cat-D" 4) "85" # zinterstore name destName numKeys key1 key2 ... [aggregate sum|max|min] redis:6379> zunionstore cat-union 2 cat cat2 (integer) 5 redis:6379> zscan cat-union 0 1) "0" 2) 1) "cat-A" 2) "10" 3) "cat-C" 4) "30" 5) "cat-B" 6) "35" 7) "cat-E" 8) "55" 9) "cat-D" 10) "85" redis:6379> zunionstore cat-union 2 cat cat2 aggregate max (integer) 5 redis:6379> zscan cat-union 0 1) "0" 2) 1) "cat-A" 2) "10" 3) "cat-B" 4) "20" 5) "cat-C" 6) "30" 7) "cat-D" 8) "45" 9) "cat-E" 10) "55"
zincrby
增减某个集合元素的score,若元素不存在,则插入# zincrby name increment value redis:6379> zincrby cat 3 cat-A "13" redis:6379> zscore cat cat-A "13"
zrem
/zremrangebyrank
按索引/zremrangebyscore
按score: 删除集合的元素# zrem name value redis:6379> zrem cat cat-B (integer) 1 redis:6379> zscan cat 0 1) "0" 2) 1) "cat-A" 2) "13" 3) "cat-C" 4) "30" 5) "cat-D" 6) "40" # zremrangebyscore name minscore maxscore redis:6379> zremrangebyscore cat 15 35 (integer) 1 redis:6379> zscan cat 0 1) "0" 2) 1) "cat-A" 2) "13" 3) "cat-D" 4) "40"
应用举例
多个String型键存储一个Object
key value post:42:title Test Article post:42:author Tom post:42:content This is Test posts:count 3 一个Hash型存储一个Object
key field value post:42 title Test Article post:42 author Tom post:42 content This is Test List
key value posts:list 1,2,42 posts:42:comments "Good","Thanks","Useful",... Set
key value post:1:tags java post:2:tags java,mysql post:42:tags java,redis tags.java.posts 1,2,42 tags.mysql.posts 2 tags.redis.posts 42 ZSet
key member score posts:readCount 1 2 posts:readCount 2 5 posts:readCount 42 10