redis + 主从 + 持久化 + 分片 + 集群 + spring集成

时间:2024-06-13 21:06:38

Redis是一个基于内存的数据库,其不仅读写速度快,每秒可以执行大约110000的写操作,81000的读取操作,而且其支持存储字符串,哈希结构,链表,集合丰富的数据类型。所以得到很多开发者的青睐。加之其支持主从、持久化等功能,3.0版本开始正式提供分片技术、让其在大型互联网应用中大显身手,本文通过实际操作和理论相配合,对redis进行详细地阐述。

一、redis的安装与使用

下载直接去redis的官网http://redis.io/进行不同操作系统对应的版本。本文中采用的redis的版本为3.2.5、linux平台,安装过程如下

[root@hbase usr]# tar -zxf redis-3.2.5.tar.gz
[root@hbase usr]# cd redis-3.2.5
[root@hbase redis-3.2.5]# ll
[root@hbase redis-3.2.5]# make
[root@hbase redis-3.2.5]# cd src
[root@hbase src]# ll

之后我们会发现其中redis-server和redis-cli,这两个文件分别对应启动redis的服务端和客户端,启动服务端

[root@hbase src]# ./redis-server
11579:M 13 Nov 15:07:01.399 # Warning: 32 bit instance detected but no memory limit set. Setting 3 GB maxmemory limit with 'noeviction' policy now.
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.5 (00000000/0) 32 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in standalone mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 6379
| `-._ `._ / _.-' | PID: 11579
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-' 11579:M 13 Nov 15:07:01.404 # Server started, Redis version 3.2.5
11579:M 13 Nov 15:07:01.409 * The server is now ready to accept connections on port 6379

可以看到,redis正常启动,6379 是 redis 服务端口,这个端口在redis.conf中可以进行配置,稍后我们讲解配置文件的时候会提到。不要关闭这个窗口,因为当前redis-server不是在后台运行,我们另起一个窗口,在当前目录下进行客户端连接服务端。

[hadoop@hbase src]$ ./redis-cli
127.0.0.1:6379>

说明一切正常,linux环境下的redis安装成功,至于windows下的安装过程相对更加简单,只需打开.exe文件即可,不在详细演示

二、redis数据类型

2.1 string

字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,在Redis中字符串类型的Value最多可以容纳的数据长度是512M。除了get、set、作外,Redis还提供了其他的一些诸如追加、递增等功能。

set key value O(1) 设定该key对应的value,如果该Key已经存在,则覆盖其原有值。get key O(1) 获取指定Key的value,如果该Key不存在,返回nil。

setnx key value O(1) 如果指定的Key不存在,则设定该Key持有指定字符串value,此时其效果等价于set命令。如果该Key已经存在,该命令将不做任何操作。

 127.0.0.1:6379> set hello word
OK
127.0.0.1:6379> get hello
"word"
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
127.0.0.1:6379> get world
(nil)
127.0.0.1:6379> set hello world_new
OK
127.0.0.1:6379> get hello
"world_new"
127.0.0.1:6379> setnx hello nihao
(integer) 0
127.0.0.1:6379> setnx new_hello nihao
(integer) 1

mset key value [key value ...] O(N) N表示指定Key的数量。该命令可以看成是多次迭代执行set命令。

mget key [key ...] O(N) N表示获取Key的数量。返回所有指定key的value,如果其中某个key不存在,该key的value将返回nil。

msetnx key value [key value ...] O(N) N表示指定key的数量。该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行setnx命令。如果在这一批keys中有任意一个key已经存在,那么该操作将全部回滚,即所有的修改都不会生效。 1表示所有keys都设置成功,0则表示没有任何key被修改。

 127.0.0.1:6379> mset key1 hello key2 world
OK
127.0.0.1:6379> mget key1 key2
1) "hello"
2) "world"
127.0.0.1:6379> mget key1 key3
1) "hello"
2) (nil)
127.0.0.1:6379> msetnx key1 nihao key3 hi
(integer) 0
127.0.0.1:6379> msetnx key3 nihao key4 hi
(integer) 1

append key value  O(1) 若key已经存在,将value的数据追加到对应key的value的末尾。如果该key不存在,append命令将会创建一个新的key/value。

strlen key O(1) 返回指定Key的字符值长度,如果该Key不存在,返回0。

decr key O(1) 将指定key的value原子性的递减1。如果该key不存在,其初始值为0,在decr之后其值为-1。如果value的值不能转换为整型值,该操作将执行失败。

incr key O(1) 将指定key的value原子性的递增1。如果该Key不存在,其初始值为0,在incr之后其值为1。如果value的值不能转换为整型值,该操作将执行失败。

decrby key decrement O(1) 将指定key的value原子性的减少decrement,其他同decr。

incrby key increment O(1) 将指定key的value原子性的增加increment,其他同incr。

 127.0.0.1:6379> append k1 hello
(integer) 5
127.0.0.1:6379> append k1 world
(integer) 10
127.0.0.1:6379> get k1
"helloworld"
127.0.0.1:6379> strlen k1
(integer) 10
127.0.0.1:6379> set k2 1
OK
127.0.0.1:6379> incr k2
(integer) 2
127.0.0.1:6379> get k2
"2"
127.0.0.1:6379> decr k2
(integer) 1
127.0.0.1:6379> get k2
"1"
127.0.0.1:6379> incrby k2 5
(integer) 6
127.0.0.1:6379> decrby k2 5
(integer) 1
127.0.0.1:6379> get k2
"1"

2.2 redis之数据类型list

在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素,该操作也可以在常量时间内完成。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。

lpush key value [value ...] O(1) 在指定key所对应的List头部插入所有values。如果该Key不存在,则插入之前创建一个与该key关联的空链表。

lpop key O(1) 返回并弹出指定key对应链表的第一个元素。如果该Key不存在,返回nil。

lrange key start stop start和end都是0-len,即0表示链表头部的第一个元素。其中start的值也可以为负值,-1将表示链表中的最后一个元素,即尾部元素,-2表示倒数第二个并以此类推。该命令在获取元素时,start和end位置上的元素也会被取出。如果start的值大于链表中元素的数量,空链表将会被返回。如果end的值大于元素的数量,该命令则获取从start(包括start)开始,链表中剩余的所有元素。

lpushx key value O(1) 当指定的key存在时,在其所关联的list的头部插入参数中给出的value,否则将不会有任何操作发生。

lrem key count value O(N) 在指定key关联的链表中,删除前count个值等于value的元素。如果count大于0,从头向尾遍历并删除,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。如果指定的key不存在,则直接返回0。

llen key O(1) 返回指定key关联的链表中元素的数量,如果该Key不存在,则返回0。。

lset  key index value O(N) 设定链表中指定位置的值为新值,其中0表示第一个元素,即头部元素,-1表示尾部元素。

 127.0.0.1:6379> lpush k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> lrange k1 0 2
1) "v3" 2) "v2" 3) "v1"
127.0.0.1:6379> lpop k1
"v3"
127.0.0.1:6379> lrange k1 0 2
1) "v2" 2) "v1"
127.0.0.1:6379> lpush k1 v4
(integer) 3
127.0.0.1:6379> lrange k1 0 2
1) "v4" 2) "v2" 3) "v1"
127.0.0.1:6379> lpush k1 v4
(integer) 4
127.0.0.1:6379> lpush k1 v4
(integer) 5
127.0.0.1:6379> lpush k1 v4
(integer) 6
127.0.0.1:6379> lrange k1 0 5
1) "v4" 2) "v4" 3) "v4" 4) "v4" 5) "v2" 6) "v1"
127.0.0.1:6379> lrem k1 2 v4 # 删除前两个值为v4的元素
(integer) 2
127.0.0.1:6379> lrange k1 0 3
1) "v4" 2) "v4" 3) "v2" 4) "v1"
127.0.0.1:6379> llen k1
(integer) 4
127.0.0.1:6379> lset k1 1 k5 #设置索引为1的值为k5
OK
127.0.0.1:6379> lrange k1 0 3
1) "v4" 2) "k5" 3) "v2" 4) "v1"

rpush key value [value ...] O(1) 在指定key所对应的List尾部插入所有values。如果该Key不存在,则插入之前创建一个与该key关联的空链表。

rpop key O(1) 返回并弹出指定key对应链表的最后一个元素。如果该Key不存在,返回nil。

rpushx key value O(1) 当指定的key存在时,在其所关联的list的尾部插入参数中给出的value,否则将不会有任何操作发生。

 127.0.0.1:6379> lrange k1 0 3
1) "v4" 2) "k5" 3) "v2" 4) "v1"
127.0.0.1:6379> rpushx k1 k6
(integer) 5
127.0.0.1:6379> lrange k1 0 4
1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6"
127.0.0.1:6379> rpush k1 k7 k8 #在尾部添加元素
(integer) 7
127.0.0.1:6379> lrange k1 0 6
1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6" 6) "k7" 7) "k8"
127.0.0.1:6379> rpop k1 #弹出尾部的元素
"k8"
127.0.0.1:6379> lrange k1 0 5
1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6" 6) "k7"
127.0.0.1:6379>

2.3 hash

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。每一个Hash可以存储4294967295个键值对。

hset key field value O(1) 为指定的key设置field/value对,若key不存在,则创建新key,并创建field/value对,若该key中已经存在,则用新值覆盖其原有值。

hget key field O(1) 返回指定key中指定field所对应的值,若key或field不存在,返回nil。

hmget key field [field ...] O(N) 获取指定fields关联的一组values。如果请求的field不存在,其值返回nil。

hmset key field value [field value ...] O(N) 逐个依次设置参数中给出的field/value对。如果其中某个field已经存在,则用新值覆盖原有值。如果key不存在,则创建新key,同时设定参数中的field/value。

hsetnx key field value O(1) 只有当key或field不存在时,为指定的key设定field/value对,否则该命令不会进行任何操作。

 127.0.0.1:6379> hset k1 f1 v1
(integer) 1
127.0.0.1:6379> hset k1 f2 v2
(integer) 1
127.0.0.1:6379> hget k1 f1
"v1"
127.0.0.1:6379> hmget k1 f1 f2
1) "v1" 2) "v2"
127.0.0.1:6379> hmset k1 f3 v3 f4 v4
OK
127.0.0.1:6379> hmget k1 f1 f2 f3 f4
1) "v1" 2) "v2" 3) "v3" 4) "v4"
127.0.0.1:6379> hsetnx k1 f1 v5
(integer) 0
127.0.0.1:6379> hsetnx k1 f5 v5
(integer) 1
127.0.0.1:6379> hmget k1 f1 f2 f3 f4 f5
1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "v5"

hkeys key O(N) 返回指定key的所有fields名。

hvals key O(N) 返回指定Key的所有values名。

hexists key field O(1) 判断指定key中的指定field是否存在。1表示存在,0表示field或key不存在。

hlen key O(1) 获取该key所包含的field的数量。返回key包含的field数量,如果key不存在,返回0。

hdel key field [field ...] O(N) 从指定key的hash中删除指定的多个字段,如果不存在的字段将被忽略。

hincrby key field increment O(1) 增加指定key中指定field对应的value的值。如果key或field不存在,该命令将会创建一个新key或新field。

 127.0.0.1:6379> hkeys k1
1) "f1" 2) "f2" 3) "f3" 4) "f4" 5) "f5"
127.0.0.1:6379> hvals k1
1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "v5"
127.0.0.1:6379> hexists k1 f1
(integer) 1
127.0.0.1:6379> hlen k1
(integer) 5
127.0.0.1:6379> hdel k1 f5
(integer) 1
127.0.0.1:6379> hset k1 f5 1
(integer) 1
127.0.0.1:6379> hvals k1
1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "1"
127.0.0.1:6379> hincrby k1 f5 2
(integer) 3
127.0.0.1:6379> hvals k1
1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "3"

2.4 set

在Redis中, Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。set可包含的最大元素数量是4294967295。set集合中不允许出现重复的元素。

sadd key member [member ...] O(N) 若该Key不存在,该命令将会创建一个新的set。若有的成员在set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。

spop key O(1) 随机的移除并返回set中的某一成员。

scard key O(1) 获取set中成员的数量。

sismember key member O(1) 判断参数中指定成员是否已经存在于与key所在的set集合中。

smembers key O(N) 获取与该key关联的set中所有的成员。

srem key member [member ...] O(N) 从与key关联的set中删除参数中指定的成员,不存在的参数成员将被忽略。

 127.0.0.1:6379> sadd k1 m1
(integer) 1
127.0.0.1:6379> sadd k1 m2
(integer) 1
127.0.0.1:6379> scard k1
(integer) 2
127.0.0.1:6379> spop k1
"m2"
127.0.0.1:6379> sadd k1 m3
(integer) 1
127.0.0.1:6379> sismember k1 m3
(integer) 1
127.0.0.1:6379> smembers k1
1) "m3"
2) "m1"
127.0.0.1:6379> srem k1 m1
(integer) 1
127.0.0.1:6379> smembers k1
1) "m3"

sdiff key [key ...] O(N) 返回第一个key所关联的set和其后所有keys所关联的sets中成员的差异。

sinter  key [key ...] O(N*M) 返回参数中所有keys关联的sets中成员的交集。

sunion key [key ...] O(N) 返回参数中所有keys关联的sets中成员的并集。

127.0.0.1:6379> smembers k1
1) "m2"
2) "m1"
3) "m3"
127.0.0.1:6379> smembers k2
1) "m3"
2) "m1"
3) "m4"
127.0.0.1:6379> sdiff k1 k2
1) "m2"
127.0.0.1:6379> sinter k1 k2
1) "m1"
2) "m3"
127.0.0.1:6379> sunion k1 k2
1) "m3"
2) "m1"
3) "m2"
4) "m4"

2.5 sorted set

sorted-set和set类型极为相似,都不允许重复的成员出现在一个Set中。它们之间的主要差别是sorted-set中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。

zadd key score member [score] [member] O(log(N)) 添加参数中指定的所有成员及其分数到指定key的sorted-sett中。

zcard key O(1) 获取与该key相关联的sorted-set中包含的成员数量。

zscore key member O(1) 获取指定Key的指定成员的分数。如果该成员存在,以字符串的形式返回其分数,否则返回nil。

zcount key min max O(log(N)+M) 用于获取分数(score)在min和max之间的成员数量。min和max表示的范围是闭区间范围,即min <= score <= max内的成员将被返回。

zincrby key increment member O(log(N)) 将为指定Key中的指定成员增加指定的分数。

zrange key start stop [WITHSCORES] O(log(N)+M)  返回顺序在start和stop指定范围内的成员列表。

zrangebyscore key min max [WITHSCORES] [LIMIT offset count] O(log(N)+M) 将返回分数在min和max之间的所有成员列表。

zrem key member [member ...] O(M log(N)) 移除参数中指定的成员,其中不存在的成员将被忽略。

zremrangebyscore key min max O(log(N)+M) 删除分数在min和max之间的所有成员,即满足表达式min <= score <= max的所有成员。

 127.0.0.1:6379> zadd k1 1 m1
(integer) 1
127.0.0.1:6379> zadd k1 2 m2
(integer) 1
127.0.0.1:6379> zadd k1 3 m3
(integer) 1
127.0.0.1:6379> zcard k1
(integer) 3
127.0.0.1:6379> zscore k1 m1
"1"
127.0.0.1:6379> zscore k1 m2
"2"
127.0.0.1:6379> zcount k1 2 3
(integer) 2
127.0.0.1:6379> zincrby k1 3 m3
"6"
127.0.0.1:6379> zrange k1 1 10
1) "m2"
2) "m3"
127.0.0.1:6379> zrange k1 0 10
1) "m1"
2) "m2"
3) "m3"
127.0.0.1:6379> zcount k1 0 10
(integer) 3
127.0.0.1:6379> zrangebyscore k1 0 6
1) "m1"
2) "m2"
3) "m3"
127.0.0.1:6379> zrem k1 m1
(integer) 1
127.0.0.1:6379> zcard k1
(integer) 2
127.0.0.1:6379> zremrangebyscore k1 2 3
(integer) 1
127.0.0.1:6379> zremrangebyscore k1 2 10
(integer) 1
127.0.0.1:6379> zcard k1
(integer) 0

三、redis的相关命令 

keys pattern O(N) 获取所有匹配pattern参数的Keys。需要说明的是,在我们的正常操作中应该尽量避免对该命令的调用,因为对于大型数据库而言,该命令是非常耗时的。pattern支持glob-style的通配符格式,如*表示任意一个或多个字符,?表示任意字符,[abc]表示方括号中任意一个字母。
del key [key ...] O(N) 从数据库删除中参数中指定的keys,如果指定键不存在,则直接忽略。
exists key O(1) 判断指定键是否存在。 1表示存在,0表示不存在。
move key db O(1) 将当前数据库中指定的键key移动到参数中指定的数据库中。若该key在目标数据库中已经存在,或者在当前数据库中不存在,该命令将不做任何操作并返回0。 移动成功返回1,否则0。
rename key newkey O(1) 为指定指定的键重新命名,如果两个keys的命令相同,或者是源key不存在,该命令都会返回相关的错误信息。如果newKey已经存在,则直接覆盖。
renamenx key newkey O(1) 如果新值不存在,则将参数中的原值修改为新值。其它条件和rename一致。1表示修改成功,否则0。
expire key seconds O(1) 该命令为指定的key设定超时的秒数,在超过该时间后,key被自动的删除。如果该Key在超时之前被修改,与该键关联的超时将被移除。1表示超时被设置,0则表示Key不存在,或不能被设置。
expireat key timestamp O(1) 该命令的逻辑功能和expire完全相同,差别是该命令指定的超时时间是绝对时间,而不是相对时间。该时间参数是Unix timestamp格式的,即从1970年1月1日开始所流经的秒数。1表示超时被设置,0则表示Key不存在,或不能被设置。
ttl key O(1) 获取该键所剩的超时描述。如果该键不存在或没有超时设置,则返回-1。
type key O(1) 获取与参数中指定键关联值的类型,该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash和zset,如果key不存在返回none。

 127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set str string
OK
127.0.0.1:6379> hset hash1 f1 v1
(integer) 1
127.0.0.1:6379> sadd set1 v1
(integer) 1
127.0.0.1:6379> zadd sortedset1 1 v1
(integer) 1
127.0.0.1:6379> lpush list1 v1 v2
(integer) 2
127.0.0.1:6379> keys *
1) "hash1"
2) "sortedset1"
3) "list1"
4) "str"
5) "set1"
127.0.0.1:6379> keys s*
1) "sortedset1"
2) "str"
3) "set1"
127.0.0.1:6379> exists list1
(integer) 1
127.0.0.1:6379> rename list1 list2
OK
127.0.0.1:6379> keys *
1) "hash1"
2) "sortedset1"
3) "str"
4) "list2"
5) "set1"
127.0.0.1:6379> type list2
list
127.0.0.1:6379> type str
string
127.0.0.1:6379> set str2 v2
OK
127.0.0.1:6379> expire str2 10
(integer) 1
127.0.0.1:6379> ttl str2
(integer) 6
127.0.0.1:6379> ttl str2
(integer) -2
127.0.0.1:6379> keys *
1) "hash1"
2) "sortedset1"
3) "str"
4) "list2"
5) "set1"
127.0.0.1:6379> move str 1
(integer) 1
127.0.0.1:6379> keys *
1) "hash1"
2) "sortedset1"
3) "list2"
4) "set1"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "str"
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> del set1
(integer) 1
127.0.0.1:6379> keys *
1) "hash1"
2) "sortedset1"
3) "list2"

四、redis的事务

1). 所有命令顺序执行,期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
2). 和关系型数据库中的事务不同的是,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
3). 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。

multi 用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行exec时,这些命令才会被原子的执行。 始终返回OK
exec 执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了watch命令,那么只有当watch所监控的keys没有被修改的前提下,exec命令才能执行事务队列中的所有命令,否则exec将放弃当前事务中的所有命令。 原子性的返回事务中各条命令的返回结果。如果在事务中使用了watch,一旦事务被放弃,exec将返回NULL-multi-bulk回复。

 127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 string
QUEUED
#由于k1为字符串类型,所以incr会报错,但是以后其他命令正常执行
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> set k1 newstring
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "newstring"

discard 回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果watch命令被使用,该命令将unwatch所有的Keys。
watch key [key ...] O(1) 在multi命令执行之前,可以指定待监控的keys,然而在执行exec之前,如果被监控的keys发生修改,exec将放弃执行该事务队列中的所有命令。
unwatch O(1) 取消当前事务中指定监控的keys,如果执行了exec或discard命令,则无需手工执行该命令,因为事务中所有被监控的keys都将自动取消。

 127.0.0.1:6379> set id 1
OK
127.0.0.1:6379> set name n1
OK
127.0.0.1:6379> set age 12
OK
127.0.0.1:6379> watch id
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name n2
QUEUED
127.0.0.1:6379> incr age
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get age
"12"
127.0.0.1:6379> get name
"n1"

注意,在提交事务之前,则执行exec命令之前,我们在另一个窗口对id进行修改(set id 2),可以看出,该事务没有执行成功

五、redis的主从复制

1). 同一个Master可以同步多个Slaves节点。

2). Slave同样可以接受其它Slaves的连接和同步请求。

3). Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。

4). Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。

5). 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。从而实现读写分离

在Slave启动并连接到Master之后,它将主动发送一个SYNC命令。此后Master将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave服务器在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。若Master和Slave之间的链接出现断连现象,Slave可以自动重连Master,但是在连接成功之后,一次完全同步将被自动执行。

同时启动两个Redis服务器,可以考虑在同一台机器上启动两个Redis服务器,分别监听不同的端口,如6379和6380。复制redis.conf并命名为slave.conf,编辑该配置文件,在84行位置将其中监听的端口改为6380:配置如下port 6380,分配启动两个server,启动时可以将配置文件作为启动命令的参数。命令如下:redis-server redis.conf ,当我们把两个server都启动时,可以进行以下步骤:

 redis-cli -p 6380

通过不同的端口来链接服务端,此时,当前客户端链接的6380端口的服务端,我们让这个服务端当做slave节点的角色。执行如下命令

slaveof 127.0.0.1 6379

当返回ok时,这里我Master-Slave建立成功。在监听端口为6379的服务器上我们用客户端进行操作

 [root@hbase redis-3.2.5]# src/redis-cli -p 6379
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> hset k2 f2 v2
(integer) 1
127.0.0.1:6379>

在slave节点,获取该值:

 [root@hbase redis-3.2.5]# src/redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> keys *
1) "k1"
2) "k2"
127.0.0.1:6380> get k1
"v1"

说明两个节点主从已经同步。实现了主从同步的效果。需要注意的是:上面的方式只是保证了在执行slaveof命令之后,redis_6380成为了redis_6379的slave,一旦服务(redis_6380)重新启动之后,他们之间的复制关系将终止。如果希望长期保证这两个服务器之间的Replication关系,可以在redis_6380的配置文件中做如下修改:

# slaveof <masterip> <masterport>改为slaveof 127.0.0.1 6379

保存退出。这样就可以保证Redis_6380服务程序在每次启动后都会主动建立与Redis_6379的Replication连接了。

六、redis的持久化

6.1、Redis提供的持久化机制:

1). RDB持久化:该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。

2). AOF持久化:该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。

6.2、RDB机制的优势和劣势:

优势:
1). 采用该方式,整个Redis数据库将只包含一个文件,通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
2). 性能最大化,对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,再由子进程完成这些持久化的工作,这样可以极避免服务进程执行IO操作。
劣势:

1). 如果你想保证数据的高可用性,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2). 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务一定的时间。

6.3、AOF机制的优势和劣势:

优势
1). 带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。

2). 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,在Redis下一次启动之前,可以通过redis-check-aof工具来解决数据一致性的问题。

3). 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4). AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。
劣势
1). 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。
2). 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。

介绍完理论之后,我们配置redis的持久化方案:

rdb方案,在redis.conf中如下配置(默认配置)

 save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

aof方案的配置:

 在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。

七、redis的分片

分片(partitioning)就是将你的数据拆分到多个 Redis 实例的过程,Redis 引入另一种哈希槽(hash slot)的概念。Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法得出结果,然后对 16384 求余数,这样每个 key 对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同节点。

原来的redis分片实现:

客户端分片(Client side partitioning),客户端直接选择正确的节点来写入和读取指定键。

代理协助分片(Proxy assisted partitioning),客户端发送请求到一个可以理解 Redis 协议的代理上,而不是直接发送请求到 Redis 实例上。代理会根据配置好的分片模式,来保证转发请求到正确的 Redis 实例,并返回响应给客户端。

查询路由(Query routing),发送查询到一个随机实例,这个实例会保证转发查询到正确的节点。

新版本Redis的解决办法:

Redis3.0版的一大特性就是支持集群(Cluster)功能。Redis集群是自动分片和高可用的首选方式。集群的特点在于拥有和单机实例同样的功能,同时在网络分区后能够提供一定的可访问性以及对主数据库故障恢复的支持。

======================= 以下概念摘自网络===================

集群节点属性: 

每个节点在集群中都有一个独一无二的 ID , 该 ID 是一个十六进制表示的 160 位随机数, 在节点第一次启动时由 /dev/urandom 生成。节点会将它的 ID 保存到配置文件, 只要这个配置文件不被删除, 节点就会一直沿用这个 ID 。节点 ID 用于标识集群中的每个节点。 一个节点可以改变它的 IP 和端口号, 而不改变节点 ID 。 集群可以自动识别出 IP/端口号的变化, 并将这一信息通过 Gossip 协议广播给其他节点知道。

节点握手:

节点总是应答(accept)来自集群连接端口的连接请求, 并对接收到的 PING 数据包进行回复, 即使这个 PING 数据包来自不可信的节点。然而, 除了 PING 之外, 节点会拒绝其他所有并非来自集群节点的数据包。要让一个节点承认另一个节点同属于一个集群, 只有以下两种方法:

1、一个节点可以通过向另一个节点发送 MEET 信息, 来强制让接收信息的节点承认发送信息的节点为集群中的一份子。 一个节点仅在管理员显式地向它发送CLUSTER MEET ip port 命令时, 才会向另一个节点发送 MEET 信息。

2、如果一个可信节点向另一个节点传播第三者节点的信息, 那么接收信息的那个节点也会将第三者节点识别为集群中的一份子。 也即是说, 如果 A 认识 B , B 认识 C , 并且 B 向 A 传播关于 C 的信息, 那么 A 也会将 C 识别为集群中的一份子, 并尝试连接 C 。

这意味着如果我们将一个/一些新节点添加到一个集群中, 那么这个/这些新节点最终会和集群中已有的其他所有节点连接起来。这说明只要管理员使用 CLUSTER MEET 命令显式地指定了可信关系, 集群就可以自动发现其他节点。这种节点识别机制通过防止不同的 Redis 集群因为 IP 地址变更或者其他网络事件的发生而产生意料之外的联合(mix), 从而使得集群更具健壮性。当节点的网络连接断开时, 它会主动连接其他已知的节点。

MOVED 转向:

一个 Redis 客户端可以向集群中的任意节点(包括从节点)发送命令请求。 节点会对命令请求进行分析, 如果该命令是集群可以执行的命令, 那么节点会查找这个命令所要处理的键所在的槽。如果要查找的哈希槽正好就由接收到命令的节点负责处理, 那么节点就直接执行这个命令。如果所查找的槽不是由该节点处理的话, 节点将查看自身内部所保存的哈希槽到节点 ID 的映射记录, 并向客户端回复一个 MOVED 错误。为了让客户端的转向操作尽可能地简单, 节点在 MOVED 错误中直接返回目标节点的 IP 和端口号, 而不是目标节点的 ID 。客户端应该记录(memorize)下“槽 3999 由节点 127.0.0.1:6381 负责处理“这一信息, 这样当再次有命令需要对槽 3999 执行时, 客户端就可以加快寻找正确节点的速度。当集群处于稳定状态时, 所有客户端最终都会保存有一个哈希槽至节点的映射记录(map of hash slots to nodes), 使得集群非常高效: 客户端可以直接向正确的节点发送命令请求, 无须转向、代理或者其他任何可能发生单点故障(single point failure)的实体(entiy)。除了 MOVED 转向错误之外, 一个客户端还应该可以处理稍后介绍的 ASK 转向错误。

集群在线重配置:

Redis 集群支持在集群运行的过程中添加或者移除节点。实际上, 节点的添加操作和节点的删除操作可以抽象成同一个操作, 那就是, 将哈希槽从一个节点移动到另一个节点。因此, 实现 Redis 集群在线重配置的核心就是将槽从一个节点移动到另一个节点的能力。 因为一个哈希槽实际上就是一些键的集合, 所以 Redis 集群在重哈希(rehash)时真正要做的, 就是将一些键从一个节点移动到另一个节点。要理解 Redis 集群如何将槽从一个节点移动到另一个节点, 我们需要对 CLUSTER 命令的各个子命令进行介绍, 这些命理负责管理集群节点的槽转换表(slots translation table)。以下是 CLUSTER 命令可用的子命令:

CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]

CLUSTER DELSLOTS slot1 [slot2] ... [slotN]

CLUSTER SETSLOT slot NODE node

CLUSTER SETSLOT slot MIGRATING node

CLUSTER SETSLOT slot IMPORTING node

最开头的两条命令 ADDSLOTS 和 DELSLOTS 分别用于向节点指派(assign)或者移除节点, 当槽被指派或者移除之后, 节点会将这一信息通过 Gossip 协议传播到整个集群。 ADDSLOTS 命令通常在新创建集群时, 作为一种快速地将各个槽指派给各个节点的手段来使用。CLUSTER SETSLOT slot NODE node 子命令可以将指定的槽 slot 指派给节点 node 。至于 CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOT slot IMPORTING node 命令, 前者用于将给定节点 node 中的槽 slot 迁移出节点, 而后者用于将给定槽 slot导入到节点 node :当一个槽被设置为 MIGRATING 状态时, 原来持有这个槽的节点仍然会继续接受关于这个槽的命令请求, 但只有命令所处理的键仍然存在于节点时, 节点才会处理这个命令请求。如果命令所使用的键不存在与该节点, 那么节点将向客户端返回一个 -ASK 转向(redirection)错误, 告知客户端, 要将命令请求发送到槽的迁移目标节点。当一个槽被设置为 IMPORTING 状态时, 节点仅在接收到 ASKING 命令之后, 才会接受关于这个槽的命令请求。如果客户端没有向节点发送 ASKING 命令, 那么节点会使用 -MOVED 转向错误将命令请求转向至真正负责处理这个槽的节点。上面关于 MIGRATING 和 IMPORTING 的说明有些难懂, 让我们用一个实际的实例来说明一下。假设现在, 我们有 A 和 B 两个节点, 并且我们想将槽 8 从节点 A 移动到节点 B , 于是我们:向节点 B 发送命令 CLUSTER SETSLOT 8 IMPORTING A,向节点 A 发送命令 CLUSTER SETSLOT 8 MIGRATING B,每当客户端向其他节点发送关于哈希槽 8 的命令请求时, 这些节点都会向客户端返回指向节点 A 的转向信息:如果命令要处理的键已经存在于槽 8 里面, 那么这个命令将由节点 A 处理。如果命令要处理的键未存在于槽 8 里面(比如说,要向槽添加一个新的键), 那么这个命令由节点 B 处理。这种机制将使得节点 A 不再创建关于槽 8 的任何新键。与此同时, 一个特殊的客户端 redis-trib 以及 Redis 集群配置程序(configuration utility)会将节点 A 中槽 8 里面的键移动到节点 B 。

键的移动操作由以下两个命令执行:

CLUSTER GETKEYSINSLOT slot count

上面的命令会让节点返回 count 个 slot 槽中的键, 对于命令所返回的每个键, redis-trib 都会向节点 A 发送一条 MIGRATE 命令, 该命令会将所指定的键原子地(atomic)从节点 A 移动到节点 B (在移动键期间,两个节点都会处于阻塞状态,以免出现竞争条件)。

以下为 MIGRATE 命令的运作原理:

MIGRATE target_host target_port key target_database id timeout执行 MIGRATE 命令的节点会连接到 target 节点, 并将序列化后的 key 数据发送给 target , 一旦 target 返回 OK , 节点就将自己的 key 从数据库中删除。从一个外部客户端的视角来看, 在某个时间点上, 键 key 要么存在于节点 A , 要么存在于节点 B , 但不会同时存在于节点 A 和节点 B 。因为 Redis 集群只使用 0 号数据库, 所以当 MIGRATE 命令被用于执行集群操作时, target_database 的值总是 0 。target_database 参数的存在是为了让 MIGRATE 命令成为一个通用命令, 从而可以作用于集群以外的其他功能。我们对 MIGRATE 命令做了优化, 使得它即使在传输包含多个元素的列表键这样的复杂数据时, 也可以保持高效。不过, 尽管 MIGRATE 非常高效, 对一个键非常多、并且键的数据量非常大的集群来说, 集群重配置还是会占用大量的时间, 可能会导致集群没办法适应那些对于响应时间有严格要求的应用程序。

ASK 转向:

在之前介绍 MOVED 转向的时候, 我们说除了 MOVED 转向之外, 还有另一种 ASK 转向。当节点需要让一个客户端长期地(permanently)将针对某个槽的命令请求发送至另一个节点时, 节点向客户端返回 MOVED 转向。另一方面, 当节点需要让客户端仅仅在下一个命令请求中转向至另一个节点时, 节点向客户端返回 ASK 转向。比如说, 在我们上一节列举的槽 8 的例子中, 因为槽 8 所包含的各个键分散在节点 A 和节点 B 中, 所以当客户端在节点 A 中没找到某个键时, 它应该转向到节点 B 中去寻找, 但是这种转向应该仅仅影响一次命令查询, 而不是让客户端每次都直接去查找节点 B : 在节点 A 所持有的属于槽 8 的键没有全部被迁移到节点 B 之前, 客户端应该先访问节点 A , 然后再访问节点 B 。因为这种转向只针对 16384 个槽中的其中一个槽, 所以转向对集群造成的性能损耗属于可接受的范围。因为上述原因, 如果我们要在查找节点 A 之后, 继续查找节点 B , 那么客户端在向节点 B 发送命令请求之前, 应该先发送一个 ASKING 命令, 否则这个针对带有IMPORTING 状态的槽的命令请求将被节点 B 拒绝执行。接收到客户端 ASKING 命令的节点将为客户端设置一个一次性的标志(flag), 使得客户端可以执行一次针对 IMPORTING 状态的槽的命令请求。从客户端的角度来看, ASK 转向的完整语义(semantics)如下:

1、如果客户端接收到 ASK 转向, 那么将命令请求的发送对象调整为转向所指定的节点。

2、先发送一个 ASKING 命令,然后再发送真正的命令请求。

3、不必更新客户端所记录的槽 8 至节点的映射: 槽 8 应该仍然映射到节点 A , 而不是节点 B 。

一旦节点 A 针对槽 8 的迁移工作完成, 节点 A 在再次收到针对槽 8 的命令请求时, 就会向客户端返回 MOVED 转向, 将关于槽 8 的命令请求长期地转向到节点 B 。注意, 即使客户端出现 Bug , 过早地将槽 8 映射到了节点 B 上面, 但只要这个客户端不发送 ASKING 命令, 客户端发送命令请求的时候就会遇上 MOVED 错误, 并将它转向回节点 A 。

容错:

节点失效检测,以下是节点失效检查的实现方法:

1、当一个节点向另一个节点发送 PING 命令, 但是目标节点未能在给定的时限内返回 PING 命令的回复时, 那么发送命令的节点会将目标节点标记为 PFAIL(possible failure,可能已失效)。等待 PING 命令回复的时限称为“节点超时时限(node timeout)”, 是一个节点选项(node-wise setting)。

2、每次当节点对其他节点发送 PING 命令的时候, 它都会随机地广播三个它所知道的节点的信息, 这些信息里面的其中一项就是说明节点是否已经被标记为 PFAIL或者 FAIL 。当节点接收到其他节点发来的信息时, 它会记下那些被其他节点标记为失效的节点。 这称为失效报告(failure report)。

3、如果节点已经将某个节点标记为 PFAIL , 并且根据节点所收到的失效报告显式, 集群中的大部分其他主节点也认为那个节点进入了失效状态, 那么节点会将那个失效节点的状态标记为 FAIL 。

4、一旦某个节点被标记为 FAIL , 关于这个节点已失效的信息就会被广播到整个集群, 所有接收到这条信息的节点都会将失效节点标记为 FAIL 。

简单来说, 一个节点要将另一个节点标记为失效, 必须先询问其他节点的意见, 并且得到大部分主节点的同意才行。因为过期的失效报告会被移除, 所以主节点要将某个节点标记为 FAIL 的话, 必须以最近接收到的失效报告作为根据。

从节点选举:一旦某个主节点进入 FAIL 状态, 如果这个主节点有一个或多个从节点存在, 那么其中一个从节点会被升级为新的主节点, 而其他从节点则会开始对这个新的主节点进行复制。新的主节点由已下线主节点属下的所有从节点中自行选举产生, 以下是选举的条件:

1、这个节点是已下线主节点的从节点。

2、已下线主节点负责处理的槽数量非空。

3、从节点的数据被认为是可靠的, 也即是, 主从节点之间的复制连接(replication link)的断线时长不能超过节点超时时限(node timeout)乘以REDIS_CLUSTER_SLAVE_VALIDITY_MULT 常量得出的积。

如果一个从节点满足了以上的所有条件, 那么这个从节点将向集群中的其他主节点发送授权请求, 询问它们, 是否允许自己(从节点)升级为新的主节点。如果发送授权请求的从节点满足以下属性, 那么主节点将向从节点返FAILOVER_AUTH_GRANTED 授权, 同意从节点的升级要求:

1、发送授权请求的是一个从节点, 并且它所属的主节点处于 FAIL 状态。

2、在已下线主节点的所有从节点中, 这个从节点的节点 ID 在排序中是最小的。

3、这个从节点处于正常的运行状态: 它没有被标记为 FAIL 状态, 也没有被标记为 PFAIL 状态。

一旦某个从节点在给定的时限内得到大部分主节点的授权, 它就会开始执行以下故障转移操作:

1、通过 PONG 数据包(packet)告知其他节点, 这个节点现在是主节点了。

2、通过 PONG 数据包告知其他节点, 这个节点是一个已升级的从节点(promoted slave)。

3、接管(claiming)所有由已下线主节点负责处理的哈希槽。

4、显式地向所有节点广播一个 PONG 数据包, 加速其他节点识别这个节点的进度, 而不是等待定时的 PING / PONG 数据包。

所有其他节点都会根据新的主节点对配置进行相应的更新:

1、所有被新的主节点接管的槽会被更新。

2、已下线主节点的所有从节点会察觉到 PROMOTED 标志, 并开始对新的主节点进行复制。

3、如果已下线的主节点重新回到上线状态, 那么它会察觉到 PROMOTED 标志, 并将自身调整为现任主节点的从节点。

在集群的生命周期中, 如果一个带有 PROMOTED 标识的主节点因为某些原因转变成了从节点, 那么该节点将丢失它所带有的 PROMOTED 标识

======================= 以上概念摘自网络===================

搭建redis集群环境需要执行的ruby的脚本,所以需要安装ruby的环境。建议用yum进行安装,由于具体的操作环境不相同,所以具体操作过程还需视操作环境而定

 [root@root java]# yum install ruby
[root@root java]# yum install rubygems
[root@root java]# gem install redis

期间会出现选择选项,键入yes即可。当出现Successfully installed redis-3.3.1  1 gem installed,说明redis集群需要的ruby环境安装成功。开始搭建redis环境

创建redis-cluster文件夹,并在其中创建6379,6380,6381文件夹,修改redis.conf文件,并将其中的端口分别设置成6379,6380,6381,redis.conf中的其他配置为

daemonize yes

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

appendonly yes

结构目录如下:

 [root@storm1 java]# cd redis-cluster/
[root@storm1 redis-cluster]# ll
total 12
drwxr-xr-x. 2 root root 4096 Nov 14 05:50 6379
drwxr-xr-x. 2 root root 4096 Nov 14 05:51 6380
drwxr-xr-x. 2 root root 4096 Nov 14 05:52 6381
[root@storm1 redis-cluster]# cd 6379
[root@storm1 6379]# cat redis.conf
port 6379
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes[root@storm1 6379]# more ../6380/redis.conf
port 6380
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
[root@storm1 6379]#

分别启动这3个redis实例,并创建集群,让三个实例互相通讯:

 [root@storm1 redis-3.2.5]# src/redis-trib.rb create --replicas 0 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381
>>> Creating cluster
>>> Performing hash slots allocation on 3 nodes...
Using 3 masters:
127.0.0.1:6379
127.0.0.1:6380
127.0.0.1:6381
M: 76d37cdb954e0c293cec5c6bc27015af40d4a59f 127.0.0.1:6379
slots:0-5460 (5461 slots) master
M: f18ab812b7f9e933639b3afdab964039ddd3ceba 127.0.0.1:6380
slots:5461-10922 (5462 slots) master
M: 644f9bca1af9cc9e3f1543ab5f0b434d11ff59b0 127.0.0.1:6381
slots:10923-16383 (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join....
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: 76d37cdb954e0c293cec5c6bc27015af40d4a59f 127.0.0.1:6379
slots:0-5460 (5461 slots) master
0 additional replica(s)
M: 644f9bca1af9cc9e3f1543ab5f0b434d11ff59b0 127.0.0.1:6381
slots:10923-16383 (5461 slots) master
0 additional replica(s)
M: f18ab812b7f9e933639b3afdab964039ddd3ceba 127.0.0.1:6380
slots:5461-10922 (5462 slots) master
0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

至此redis集群即搭建成功,简单进行测试一下

 [root@storm1 redis-3.2.5]# src/redis-cli -c -p 6379
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 127.0.0.1:6381
OK
127.0.0.1:6381> exit
[root@storm1 redis-3.2.5]# src/redis-cli -c -p 6381
127.0.0.1:6381> keys *
1) "k1"
127.0.0.1:6381>

可以看到,虽然我们第一次连接的是6379端口,redis cluster 自动帮我们重定向到 6381 。在6381端口的实例中出现了我们之前设置的k1,说明整个集群是工作的。

八、redis的管理命令

config get parameter 用于读取服务器的运行时参数,但是并不是所有的配置参数都可以通过该命令进行读取。该命令的参数接受glob风格的模式匹配规则,因此如果参数中包含模式元字符,那么所有匹配的参数都将以key/value方式被列出。如果参数是*,那么该命令支持的所有参数都将被列出。

config set parameter value 该命令用于配置Redis服务器的运行时参数,在设置成功之后无需重启便可生效。然而并非所有的参数都可以通过该命令进行动态设置。
dbsize 返回当前打开的数据库中keys的数量。
flushall  清空当前服务器管理的数据库中的所有keys,不仅限于当前打开的数据库。
flushdb  清空当前数据库中的所有keys。
info  获取和服务器运行状况相关的一些列统计数字。
save 设置RDB持久化模式的保存策略。
shutdown  停止所有的客户端,同时以阻塞的方式执行内存数据持久化。如果AOF模式被启用,则将缓存中的数据flush到AOF文件。退出服务器。
slaveof host port 该命令用于修改slave服务器的主从复制设置。
slowlog subcommand [argument] 该命令主要用于读取执行时间较长的命令。由于slowlog队列不会被持久化到磁盘,因此Redis在收集命令时不会对性能产生很大的影响。通常我们可以将参数"slowlog-log-slower-than"设置为0,以便收集所有命令的执行时间。该命令还包含以下几个子命令:
1). slowlog get N: 从slowlog队列中读取命令信息,N表示最近N条命令的信息。
2). slowlog len:获取slowlog队列的长度。
3). slowlog reset:清空slowlog中的内容。

 redis 127.0.0.1:6379> config get port
1) "port"
2) "6379"
redis 127.0.0.1:6379> keys *
1) "k1"
redis 127.0.0.1:6379> flushdb
OK
redis 127.0.0.1:6379> keys *
(empty list or set)
redis 127.0.0.1:6379> dbsize
(integer) 0
redis 127.0.0.1:6379> set k1 v1
OK
redis 127.0.0.1:6379> dbsize
(integer) 1

九、redis的java操作

maven依赖

 <dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

java代码:

 import redis.clients.jedis.Jedis;

 public class RedisDao {

     public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
jedis.set("k2", "v2");
System.out.println(jedis.get("k2"));
jedis.hset("k3", "f1", "v1");
System.out.println(jedis.hget("k3", "f1"));
System.out.println(jedis.keys("*"));
jedis.close();
}
}

十、redis与spring整合 

10.1 引入jedis包和spring包

10.2 spring-mvc.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName"> <bean id="redisClient" class="com.eztcn.commons.redis.RedisClientFactoryBean">
<!-- 单个应用中的链接池最大链接数,默认8 -->
<property name="maxTotal" value="150" />
<!-- 单个应用中的链接池最大空闲数,默认8 -->
<property name="maxIdle" value="50" />
<!-- 单个应用中的链接池最大链接数,默认8 -->
<property name="minIdle" value="30" />
<!-- 设置在每一次取对象时测试ping -->
<property name="testOnBorrow" value="false" />
<!-- host:port -->
<property name="ipConfString" value="192.168.1.60:7001" />
</bean>
</beans>

当然需要在spring配置文件中引入该配置文件:<import resource="spring/spring-config-redis.xml" />

10.3 RedisClientFactoryBean.java

 package com.commons.redis;

 import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.FactoryBean; public class RedisClientFactoryBean implements FactoryBean<RedisClient> {
private RedisClientConfig redisClientConfig = new RedisClientConfig(); public RedisClient getObject() throws Exception {
if (StringUtils.isNotBlank(this.redisClientConfig.getIpConfString())) {
return new RedisClient(this.redisClientConfig);
}
throw new RedisInitializerException("RedisClient init parameter masterConfString is empty,please check spring config file!");
} public Class<?> getObjectType() {
return RedisClient.class;
} public boolean isSingleton() {
return true;
} public void setIpConfString(String string) {
this.redisClientConfig.setIpConfString(string);
} public void setMaxTotal(int maxTotal) {
this.redisClientConfig.setMaxTotal(maxTotal);
} public void setMaxIdle(int maxIdle) {
this.redisClientConfig.setMaxIdle(maxIdle);
} public void setMinIdle(int minIdle) {
this.redisClientConfig.setMinIdle(minIdle);
} public void setTestOnBorrow(boolean flag) {
this.redisClientConfig.setTestOnBorrow(flag);
}
}

10.4 RedisClientConfig.java

 package com.commons.redis;

 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

 /**
*
* @描述 : 配置redis的相关属性
* @创建时间: 2015年7月20日上午11:05:53
*
*/
public class RedisClientConfig {
private GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig(); private String ipConfString; /**
*
* @描述 : 单个应用中的链接池最大链接数
* @创建时间: 2015年7月20日上午11:11:10
*
* @param maxTotal
*/
public void setMaxTotal(int maxTotal) {
this.jedisPoolConfig.setMaxTotal(maxTotal);
} /**
* @描述 :单个应用中的链接池最大空闲数
* @创建时间: 2015年7月20日上午11:11:28
*
* @param maxIdle
*/ public void setMaxIdle(int maxIdle) {
this.jedisPoolConfig.setMaxIdle(maxIdle);
} /**
* @描述 : 单个应用中的链接池最大链接数
* @创建时间: 2015年7月20日上午11:11:43
*
* @param maxIdle
*/
public void setMinIdle(int minIdle) {
this.jedisPoolConfig.setMinIdle(minIdle);
} /**
* @描述 : 设置在每一次取对象时测试ping
* @创建时间: 2015年7月20日上午11:11:58
*
* @param flag
*/
public void setTestOnBorrow(boolean flag) {
this.jedisPoolConfig.setTestOnBorrow(flag);
} public GenericObjectPoolConfig getJedisPoolConfig() {
return this.jedisPoolConfig;
} public String getIpConfString() {
return this.ipConfString;
} public void setIpConfString(String ipConfString) {
this.ipConfString = ipConfString;
}
}

10.5 RedisClient.java

 package com.commons.redis;

 import java.util.List;
import java.util.Map;
import java.util.Set; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import redis.clients.jedis.BinaryClient;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; public class RedisClient {
private static final Log LOG = LogFactory.getLog(RedisClient.class);
private RedisClientConfig redisClientConfig;
private JedisPool jedisPool; public RedisClient(RedisClientConfig redisClientConfig) {
this.redisClientConfig = redisClientConfig;
init();
} /**
*
* @描述 : 初始化,配置ip和端口
* @创建时间: 2015年7月20日下午1:43:29
*
*/
private void init() {
LOG.info("redis client init start~");
String ip = "";
int port;
if (StringUtils.isNotBlank(this.redisClientConfig.getIpConfString())) {
String[] ipPortArray = this.redisClientConfig.getIpConfString().split(":");
if (ipPortArray.length == 1) {
throw new RedisInitializerException(ipPortArray + " is not include host:port or host:port after split \":\"");
}
ip = ipPortArray[0];
port = Integer.valueOf(ipPortArray[1]).intValue();
} else {
throw new RuntimeException("init throw exception");
}
LOG.info("write redis client connect ip:" + ip + ",port:" + port);
this.jedisPool = new JedisPool(this.redisClientConfig.getJedisPoolConfig(), ip, port);
} /**
*
* @描述 : 设置键值对
* @创建时间: 2015年7月20日下午1:44:10
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public String set(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.set(key, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX
* 命令将覆写旧值。
* @创建时间: 2015年7月20日下午1:44:36
*
* @param key
* @param seconds
* @param value
* @return
* @throws RedisAccessException
*/
public String set(String key, int seconds, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.setex(key, seconds, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 如果 key 不存在,
* APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
* @创建时间: 2015年7月20日下午1:45:55
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public Long append(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.append(key, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 null 。 假如 key
* 储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值。
* @创建时间: 2015年7月20日下午1:47:10
*
* @param key
* @return
* @throws RedisAccessException
*/
public String get(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.get(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 删除给定的一个或多个 key 。 不存在的 key 会被忽略。
* @创建时间: 2015年7月20日下午1:48:37
*
* @param key
* @return
* @throws RedisAccessException
*/
public Long del(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.del(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 检查给定 key 是否存在。
* @创建时间: 2015年7月20日下午1:48:48
*
* @param key
* @return
* @throws RedisAccessException
*/
public Boolean exists(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.exists(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
* @描述 : 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
* @创建时间: 2015年7月20日下午1:49:29
*
* @param key
* @param seconds
* @return
* @throws RedisAccessException
*/
public Long expire(String key, int seconds) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.expire(key, seconds);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
* @描述 : 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
* @创建时间: 2015年7月20日下午1:49:29
*
* @param key
* @param seconds
* @return
* @throws RedisAccessException
*/
public Long expireAt(String key, long unixTime) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.expireAt(key,unixTime);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key
* 存在但不是字符串类型时,返回一个错误。
* @创建时间: 2015年7月20日下午1:50:28
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public String getSet(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.getSet(key, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。
* @创建时间: 2015年7月20日下午1:52:07
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public Long setnx(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.setnx(key, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将哈希表 key 中的域 field 的值设为 value 。 如果 key 不存在,一个新的哈希表被创建并进行 HSET操作。
* 如果域 field 已经存在于哈希表中,旧值将被覆盖。
* @创建时间: 2015年7月20日下午1:53:23
*
* @param key
* @param field
* @param value
* @return
* @throws RedisAccessException
*/
public Long hset(String key, String field, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hset(key, field, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 :返回哈希表 key 中给定域 field 的值。
* @创建时间: 2015年7月20日下午1:54:24
*
* @param key
* @param field
* @return
* @throws RedisAccessException
*/
public String hget(String key, String field) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hget(key, field);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。若域 field
* 已经存在,该操作无效。 如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令。
* @创建时间: 2015年7月20日下午1:54:44
*
* @param key
* @param field
* @param value
* @return
* @throws RedisAccessException
*/
public Long hsetnx(String key, String field, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hsetnx(key, field, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 :同时将多个 field-value (域-值)对设置到哈希表 key 中。此命令会覆盖哈希表中已存在的域。 如果 key
* 不存在,一个空哈希表被创建并执行 HMSET 操作。
* @创建时间: 2015年7月20日下午1:58:26
*
* @param key
* @param hash
* @return
* @throws RedisAccessException
*/
public String hmset(String key, Map<String, String> hash) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hmset(key, hash);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回哈希表 key 中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个 null 值。 因为不存在的 key
* 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 null 值的表。
* @创建时间: 2015年7月20日下午1:57:32
*
* @param key
* @param fields
* @return
* @throws RedisAccessException
*/
public List<String> hmget(String key, String[] fields) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hmget(key, fields);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 :删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
* @创建时间: 2015年7月20日下午1:59:32
*
* @param key
* @param fields
* @return
* @throws RedisAccessException
*/
public Long hdel(String key, String[] fields) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hdel(key, fields);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回哈希表 key 中域的数量。
* @创建时间: 2015年7月20日下午2:00:13
*
* @param key
* @return
* @throws RedisAccessException
*/
public Long hlen(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hlen(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 :返回哈希表 key 中的所有域。
* @创建时间: 2015年7月20日下午2:00:59
*
* @param key
* @return
* @throws RedisAccessException
*/
public Set<String> hkeys(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hkeys(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回哈希表 key 中所有域的值。
* @创建时间: 2015年7月20日下午2:01:06
*
* @param key
* @return
* @throws RedisAccessException
*/
public List<String> hvals(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hvals(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名(field
* name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
* @创建时间: 2015年7月20日下午2:01:36
*
* @param key
* @return
* @throws RedisAccessException
*/
public Map<String, String> hgetAll(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hgetAll(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 :查看哈希表 key 中,给定域 field 是否存在。
* @创建时间: 2015年7月20日下午2:02:38
*
* @param key
* @param field
* @return
* @throws RedisAccessException
*/
public Boolean hexists(String key, String field) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.hexists(key, field);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
* @创建时间: 2015年7月20日下午2:02:44
*
* @param key
* @param values
* @return
* @throws RedisAccessException
*/
public Long rpush(String key, String[] values) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.rpush(key, values);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
* @创建时间: 2015年7月20日下午2:03:20
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public Long rpushx(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.rpushx(key, new String[] { value });
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将一个或多个值 value 插入到列表 key 的表头
* @创建时间: 2015年7月20日下午2:04:13
*
* @param key
* @param values
* @return
* @throws RedisAccessException
*/
public Long lpush(String key, String[] values) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.lpush(key, values);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。 和 LPUSH 命令相反,当 key 不存在时,
* LPUSHX 命令什么也不做。
* @创建时间: 2015年7月20日下午2:05:09
*
* @param key
* @param value
* @return
* @throws RedisAccessException
*/
public Long lpushx(String key, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.lpushx(key, new String[] { value });
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回列表 key 的长度。如果 key 不存在,则 key 被解释为一个空列表,返回 0 . 如果 key
* 不是列表类型,返回一个错误。
* @创建时间: 2015年7月20日下午2:05:25
*
* @param key
* @return
* @throws RedisAccessException
*/
public Long llen(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.llen(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 返回列表 key 中,下标为 index 的元素。
* @创建时间: 2015年7月20日下午2:05:37
*
* @param key
* @param index
* @return
* @throws RedisAccessException
*/
public String lindex(String key, long index) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.lindex(key, index);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将列表 key 下标为 index 的元素的值设置为 value 。当 index 参数超出范围,或对一个空列表( key
* 不存在)进行 LSET 时,返回一个错误。
* @创建时间: 2015年7月20日下午2:07:06
*
* @param key
* @param index
* @param value
* @return
* @throws RedisAccessException
*/
public String lset(String key, long index, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.lset(key, index, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 移除并返回列表 key 的头元素。
* @创建时间: 2015年7月20日下午2:07:10
*
* @param key
* @return
* @throws RedisAccessException
*/
public String lpop(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.lpop(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 移除并返回列表 key 的尾元素。
* @创建时间: 2015年7月20日下午2:07:13
*
* @param key
* @return
* @throws RedisAccessException
*/
public String rpop(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.rpop(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
*
* @描述 : 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。当 pivot 不存在于列表 key 时,不执行任何操作。
* key 不存在时, key 被视为空列表,不执行任何操作。 如果 key 不是列表类型,返回一个错误。
* @创建时间: 2015年7月20日下午2:07:37
*
* @param key
* @param where
* @param pivot
* @param value
* @return
* @throws RedisAccessException
*/
public Long linsert(String key, BinaryClient.LIST_POSITION where, String pivot, String value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.linsert(key, where, pivot, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
* @描述 : 将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
* @创建时间: 2014-6-16下午2:31:58
*
* @param key
* @return
* @throws RedisAccessException
*/
public Long incr(String key) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.incr(key);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} /**
* @描述 : 将 key 所储存的值加上增量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
* @创建时间: 2014-6-16下午2:31:58
*
* @param key
* @return
* @throws RedisAccessException
*/
public double incrBy(String key,long value) throws RedisAccessException {
Jedis client = null;
try {
client = jedisPool.getResource();
return client.incrBy(key, value);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new RedisAccessException(e);
} finally {
if (null != client) {
jedisPool.returnResourceObject(client);
}
}
} }

OK,这个关于redis的相关内容更新完毕!

本文地址: http://www.cnblogs.com/gzy-blog/p/6058849.html