zoukankan      html  css  js  c++  java
  • redis + 主从 + 持久化 + 分片 + 集群 + spring集成

    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已经存在,该命令将不做任何操作。 

     1 127.0.0.1:6379> set hello word
     2 OK
     3 127.0.0.1:6379> get hello
     4 "word"
     5 127.0.0.1:6379> set hello world
     6 OK
     7 127.0.0.1:6379> set hello world
     8 OK
     9 127.0.0.1:6379> get hello
    10 "world"
    11 127.0.0.1:6379> get world
    12 (nil)
    13 127.0.0.1:6379> set hello world_new
    14 OK
    15 127.0.0.1:6379> get hello
    16 "world_new"
    17 127.0.0.1:6379> setnx hello nihao
    18 (integer) 0
    19 127.0.0.1:6379> setnx new_hello nihao
    20 (integer) 1
    View Code

    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被修改。 

     1 127.0.0.1:6379> mset key1 hello key2 world
     2 OK
     3 127.0.0.1:6379> mget key1 key2
     4 1) "hello"
     5 2) "world"
     6 127.0.0.1:6379> mget key1 key3
     7 1) "hello"
     8 2) (nil)
     9 127.0.0.1:6379> msetnx key1 nihao key3 hi
    10 (integer) 0
    11 127.0.0.1:6379> msetnx key3 nihao key4 hi
    12 (integer) 1
    View Code

    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。 

     1 127.0.0.1:6379> append k1 hello
     2 (integer) 5
     3 127.0.0.1:6379> append k1 world
     4 (integer) 10
     5 127.0.0.1:6379> get k1
     6 "helloworld"
     7 127.0.0.1:6379> strlen k1
     8 (integer) 10
     9 127.0.0.1:6379> set k2 1
    10 OK
    11 127.0.0.1:6379> incr k2
    12 (integer) 2
    13 127.0.0.1:6379> get k2
    14 "2"
    15 127.0.0.1:6379> decr k2
    16 (integer) 1
    17 127.0.0.1:6379> get k2
    18 "1"
    19 127.0.0.1:6379> incrby k2 5
    20 (integer) 6
    21 127.0.0.1:6379> decrby k2 5
    22 (integer) 1
    23 127.0.0.1:6379> get k2
    24 "1"
    View Code

    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表示尾部元素。 

     1 127.0.0.1:6379> lpush k1 v1 v2 v3
     2 (integer) 3
     3 127.0.0.1:6379> lrange k1 0 2
     4 1) "v3" 2) "v2" 3) "v1"
     5 127.0.0.1:6379> lpop k1
     6 "v3"
     7 127.0.0.1:6379> lrange k1 0 2
     8 1) "v2" 2) "v1"
     9 127.0.0.1:6379> lpush k1 v4
    10 (integer) 3
    11 127.0.0.1:6379> lrange k1 0 2
    12 1) "v4" 2) "v2" 3) "v1"
    13 127.0.0.1:6379> lpush k1 v4
    14 (integer) 4
    15 127.0.0.1:6379> lpush k1 v4
    16 (integer) 5
    17 127.0.0.1:6379> lpush k1 v4
    18 (integer) 6
    19 127.0.0.1:6379> lrange k1 0 5
    20 1) "v4" 2) "v4" 3) "v4" 4) "v4" 5) "v2" 6) "v1"
    21 127.0.0.1:6379> lrem k1 2 v4  # 删除前两个值为v4的元素
    22 (integer) 2
    23 127.0.0.1:6379> lrange k1 0 3
    24 1) "v4" 2) "v4" 3) "v2" 4) "v1"
    25 127.0.0.1:6379> llen k1
    26 (integer) 4
    27 127.0.0.1:6379> lset k1 1 k5  #设置索引为1的值为k5
    28 OK
    29 127.0.0.1:6379> lrange k1 0 3
    30 1) "v4" 2) "k5" 3) "v2" 4) "v1"
    View Code

    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,否则将不会有任何操作发生。  

     1 127.0.0.1:6379> lrange k1 0 3
     2 1) "v4" 2) "k5" 3) "v2" 4) "v1"
     3 127.0.0.1:6379> rpushx k1 k6
     4 (integer) 5
     5 127.0.0.1:6379> lrange k1 0 4
     6 1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6"
     7 127.0.0.1:6379> rpush k1 k7 k8  #在尾部添加元素
     8 (integer) 7
     9 127.0.0.1:6379> lrange k1 0 6
    10 1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6" 6) "k7" 7) "k8"  
    11 127.0.0.1:6379> rpop k1  #弹出尾部的元素
    12 "k8"
    13 127.0.0.1:6379> lrange k1 0 5
    14 1) "v4" 2) "k5" 3) "v2" 4) "v1" 5) "k6" 6) "k7"
    15 127.0.0.1:6379> 
    View Code

    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对,否则该命令不会进行任何操作。

     1 127.0.0.1:6379> hset k1 f1 v1
     2 (integer) 1
     3 127.0.0.1:6379> hset k1 f2 v2
     4 (integer) 1
     5 127.0.0.1:6379> hget k1 f1
     6 "v1"
     7 127.0.0.1:6379> hmget k1 f1 f2
     8 1) "v1" 2) "v2"
     9 127.0.0.1:6379> hmset k1 f3 v3 f4 v4
    10 OK
    11 127.0.0.1:6379> hmget k1 f1 f2 f3 f4
    12 1) "v1" 2) "v2" 3) "v3" 4) "v4"
    13 127.0.0.1:6379> hsetnx k1 f1 v5
    14 (integer) 0
    15 127.0.0.1:6379> hsetnx k1 f5 v5
    16 (integer) 1
    17 127.0.0.1:6379> hmget k1 f1 f2 f3 f4 f5
    18 1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "v5"
    View Code

    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。 

     1 127.0.0.1:6379> hkeys k1
     2 1) "f1" 2) "f2" 3) "f3" 4) "f4" 5) "f5"
     3 127.0.0.1:6379> hvals k1
     4 1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "v5"
     5 127.0.0.1:6379> hexists k1 f1
     6 (integer) 1
     7 127.0.0.1:6379> hlen k1 
     8 (integer) 5
     9 127.0.0.1:6379> hdel k1 f5
    10 (integer) 1
    11 127.0.0.1:6379> hset k1 f5 1
    12 (integer) 1
    13 127.0.0.1:6379> hvals k1
    14 1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "1"
    15 127.0.0.1:6379> hincrby k1 f5 2
    16 (integer) 3
    17 127.0.0.1:6379> hvals k1
    18 1) "v1" 2) "v2" 3) "v3" 4) "v4" 5) "3"
    View Code

    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中删除参数中指定的成员,不存在的参数成员将被忽略。

     1 127.0.0.1:6379> sadd k1 m1
     2 (integer) 1
     3 127.0.0.1:6379> sadd k1 m2
     4 (integer) 1
     5 127.0.0.1:6379> scard k1
     6 (integer) 2
     7 127.0.0.1:6379> spop k1
     8 "m2"
     9 127.0.0.1:6379> sadd k1 m3
    10 (integer) 1
    11 127.0.0.1:6379> sismember k1 m3
    12 (integer) 1
    13 127.0.0.1:6379> smembers k1
    14 1) "m3"
    15 2) "m1"
    16 127.0.0.1:6379> srem k1 m1
    17 (integer) 1
    18 127.0.0.1:6379> smembers k1
    19 1) "m3"
    View Code

    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"
    View Code

     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的所有成员。 

     1 127.0.0.1:6379> zadd k1 1 m1
     2 (integer) 1
     3 127.0.0.1:6379> zadd k1 2 m2
     4 (integer) 1
     5 127.0.0.1:6379> zadd k1 3 m3
     6 (integer) 1
     7 127.0.0.1:6379> zcard k1
     8 (integer) 3
     9 127.0.0.1:6379> zscore k1 m1
    10 "1"
    11 127.0.0.1:6379> zscore k1 m2
    12 "2"
    13 127.0.0.1:6379> zcount k1 2 3
    14 (integer) 2
    15 127.0.0.1:6379> zincrby k1 3 m3
    16 "6"
    17 127.0.0.1:6379> zrange k1 1 10
    18 1) "m2"
    19 2) "m3"
    20 127.0.0.1:6379> zrange k1 0 10
    21 1) "m1"
    22 2) "m2"
    23 3) "m3"
    24 127.0.0.1:6379> zcount k1 0 10
    25 (integer) 3
    26 127.0.0.1:6379> zrangebyscore k1 0 6
    27 1) "m1"
    28 2) "m2"
    29 3) "m3"
    30 127.0.0.1:6379> zrem k1 m1
    31 (integer) 1
    32 127.0.0.1:6379> zcard k1
    33 (integer) 2
    34 127.0.0.1:6379> zremrangebyscore k1 2 3
    35 (integer) 1
    36 127.0.0.1:6379> zremrangebyscore k1 2 10
    37 (integer) 1
    38 127.0.0.1:6379> zcard k1
    39 (integer) 0
    View Code

    三、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。

     1 127.0.0.1:6379> keys *
     2 (empty list or set)
     3 127.0.0.1:6379> set str string
     4 OK
     5 127.0.0.1:6379> hset hash1 f1 v1
     6 (integer) 1
     7 127.0.0.1:6379> sadd set1 v1
     8 (integer) 1
     9 127.0.0.1:6379> zadd sortedset1 1 v1
    10 (integer) 1
    11 127.0.0.1:6379> lpush list1 v1 v2
    12 (integer) 2
    13 127.0.0.1:6379> keys *
    14 1) "hash1"
    15 2) "sortedset1"
    16 3) "list1"
    17 4) "str"
    18 5) "set1"
    19 127.0.0.1:6379> keys s*
    20 1) "sortedset1"
    21 2) "str"
    22 3) "set1"
    23 127.0.0.1:6379> exists list1
    24 (integer) 1
    25 127.0.0.1:6379> rename list1 list2
    26 OK
    27 127.0.0.1:6379> keys *
    28 1) "hash1"
    29 2) "sortedset1"
    30 3) "str"
    31 4) "list2"
    32 5) "set1"
    33 127.0.0.1:6379> type list2
    34 list
    35 127.0.0.1:6379> type str
    36 string
    37 127.0.0.1:6379> set str2 v2
    38 OK
    39 127.0.0.1:6379> expire str2 10
    40 (integer) 1
    41 127.0.0.1:6379> ttl str2
    42 (integer) 6
    43 127.0.0.1:6379> ttl str2
    44 (integer) -2
    45 127.0.0.1:6379> keys *
    46 1) "hash1"
    47 2) "sortedset1"
    48 3) "str"
    49 4) "list2"
    50 5) "set1"
    51 127.0.0.1:6379> move str 1
    52 (integer) 1
    53 127.0.0.1:6379> keys *
    54 1) "hash1"
    55 2) "sortedset1"
    56 3) "list2"
    57 4) "set1"
    58 127.0.0.1:6379> select 1
    59 OK
    60 127.0.0.1:6379[1]> keys *
    61 1) "str"
    62 127.0.0.1:6379[1]> select 0
    63 OK
    64 127.0.0.1:6379> del set1
    65 (integer) 1
    66 127.0.0.1:6379> keys *
    67 1) "hash1"
    68 2) "sortedset1"
    69 3) "list2"
    View Code

    四、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回复。

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

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

     1 127.0.0.1:6379> set id 1
     2 OK
     3 127.0.0.1:6379> set name n1
     4 OK
     5 127.0.0.1:6379> set age 12
     6 OK
     7 127.0.0.1:6379> watch id
     8 OK
     9 127.0.0.1:6379> multi 
    10 OK
    11 127.0.0.1:6379> set name n2
    12 QUEUED
    13 127.0.0.1:6379> incr age
    14 QUEUED
    15 127.0.0.1:6379> exec
    16 (nil)
    17 127.0.0.1:6379> get age
    18 "12"
    19 127.0.0.1:6379> get name
    20 "n1"
    View Code

     注意,在提交事务之前,则执行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都启动时,可以进行以下步骤:

    1 redis-cli -p 6380
    View Code

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

    slaveof 127.0.0.1 6379
    View Code

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

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

    在slave节点,获取该值:

    1 [root@hbase redis-3.2.5]# src/redis-cli -p 6380
    2 127.0.0.1:6380> slaveof 127.0.0.1 6379
    3 OK
    4 127.0.0.1:6380> keys *
    5 1) "k1"
    6 2) "k2"
    7 127.0.0.1:6380> get k1
    8 "v1"
    View Code

    说明两个节点主从已经同步。实现了主从同步的效果。需要注意的是:上面的方式只是保证了在执行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中如下配置(默认配置) 

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

    aof方案的配置:

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

    七、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进行安装,由于具体的操作环境不相同,所以具体操作过程还需视操作环境而定 

    1 [root@root java]# yum install ruby
    2 [root@root java]# yum install rubygems
    3 [root@root java]# gem install redis
    View Code

    期间会出现选择选项,键入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

    结构目录如下:

     1 [root@storm1 java]# cd redis-cluster/
     2 [root@storm1 redis-cluster]# ll
     3 total 12
     4 drwxr-xr-x. 2 root root 4096 Nov 14 05:50 6379
     5 drwxr-xr-x. 2 root root 4096 Nov 14 05:51 6380
     6 drwxr-xr-x. 2 root root 4096 Nov 14 05:52 6381
     7 [root@storm1 redis-cluster]# cd 6379
     8 [root@storm1 6379]# cat redis.conf 
     9 port 6379
    10 daemonize yes
    11 cluster-enabled yes
    12 cluster-config-file nodes.conf
    13 cluster-node-timeout 5000
    14 appendonly yes[root@storm1 6379]# more ../6380/redis.conf 
    15 port 6380
    16 daemonize yes
    17 cluster-enabled yes
    18 cluster-config-file nodes.conf
    19 cluster-node-timeout 5000
    20 appendonly yes
    21 [root@storm1 6379]# 
    View Code

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

     1 [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
     2 >>> Creating cluster
     3 >>> Performing hash slots allocation on 3 nodes...
     4 Using 3 masters:
     5 127.0.0.1:6379
     6 127.0.0.1:6380
     7 127.0.0.1:6381
     8 M: 76d37cdb954e0c293cec5c6bc27015af40d4a59f 127.0.0.1:6379
     9    slots:0-5460 (5461 slots) master
    10 M: f18ab812b7f9e933639b3afdab964039ddd3ceba 127.0.0.1:6380
    11    slots:5461-10922 (5462 slots) master
    12 M: 644f9bca1af9cc9e3f1543ab5f0b434d11ff59b0 127.0.0.1:6381
    13    slots:10923-16383 (5461 slots) master
    14 Can I set the above configuration? (type 'yes' to accept): yes
    15 >>> Nodes configuration updated
    16 >>> Assign a different config epoch to each node
    17 >>> Sending CLUSTER MEET messages to join the cluster
    18 Waiting for the cluster to join....
    19 >>> Performing Cluster Check (using node 127.0.0.1:6379)
    20 M: 76d37cdb954e0c293cec5c6bc27015af40d4a59f 127.0.0.1:6379
    21    slots:0-5460 (5461 slots) master
    22    0 additional replica(s)
    23 M: 644f9bca1af9cc9e3f1543ab5f0b434d11ff59b0 127.0.0.1:6381
    24    slots:10923-16383 (5461 slots) master
    25    0 additional replica(s)
    26 M: f18ab812b7f9e933639b3afdab964039ddd3ceba 127.0.0.1:6380
    27    slots:5461-10922 (5462 slots) master
    28    0 additional replica(s)
    29 [OK] All nodes agree about slots configuration.
    30 >>> Check for open slots...
    31 >>> Check slots coverage...
    32 [OK] All 16384 slots covered.
    View Code

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

    1 [root@storm1 redis-3.2.5]# src/redis-cli -c -p 6379
    2 127.0.0.1:6379> set k1 v1
    3 -> Redirected to slot [12706] located at 127.0.0.1:6381
    4 OK
    5 127.0.0.1:6381> exit
    6 [root@storm1 redis-3.2.5]# src/redis-cli -c -p 6381
    7 127.0.0.1:6381> keys *
    8 1) "k1"
    9 127.0.0.1:6381> 
    View Code

    可以看到,虽然我们第一次连接的是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中的内容。

     1 redis 127.0.0.1:6379> config get port
     2 1) "port"
     3 2) "6379"
     4 redis 127.0.0.1:6379> keys *
     5 1) "k1"
     6 redis 127.0.0.1:6379> flushdb
     7 OK
     8 redis 127.0.0.1:6379> keys *
     9 (empty list or set)
    10 redis 127.0.0.1:6379> dbsize
    11 (integer) 0
    12 redis 127.0.0.1:6379> set k1 v1
    13 OK
    14 redis 127.0.0.1:6379> dbsize
    15 (integer) 1
    View Code

    九、redis的java操作

    maven依赖 

    1 <dependency>
    2     <groupId>redis.clients</groupId>
    3     <artifactId>jedis</artifactId>
    4     <version>2.6.2</version>
    5     <type>jar</type>
    6     <scope>compile</scope>
    7 </dependency>
    View Code

     java代码:

     1 import redis.clients.jedis.Jedis;
     2 
     3 public class RedisDao {
     4 
     5     public static void main(String[] args) {
     6         Jedis jedis = new Jedis("127.0.0.1", 6379);
     7         jedis.set("k2", "v2");
     8         System.out.println(jedis.get("k2"));
     9         jedis.hset("k3", "f1", "v1");
    10         System.out.println(jedis.hget("k3", "f1"));
    11         System.out.println(jedis.keys("*"));
    12         jedis.close();
    13     }
    14 }
    View Code

    十、redis与spring整合 

    10.1 引入jedis包和spring包

    10.2 spring-mvc.xml 

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <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
     3     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName">
     4 
     5     <bean id="redisClient" class="com.eztcn.commons.redis.RedisClientFactoryBean">
     6         <!-- 单个应用中的链接池最大链接数,默认8 -->
     7         <property name="maxTotal" value="150" />
     8         <!-- 单个应用中的链接池最大空闲数,默认8 -->
     9         <property name="maxIdle" value="50" />
    10         <!-- 单个应用中的链接池最大链接数,默认8 -->
    11         <property name="minIdle" value="30" />
    12         <!-- 设置在每一次取对象时测试ping -->
    13         <property name="testOnBorrow" value="false" />
    14         <!-- host:port -->
    15           <property name="ipConfString" value="192.168.1.60:7001" /> 
    16     </bean>
    17 </beans>
    View Code

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

    10.3 RedisClientFactoryBean.java 

     1 package com.commons.redis;
     2 
     3 import org.apache.commons.lang3.StringUtils;
     4 import org.springframework.beans.factory.FactoryBean;
     5 
     6 public class RedisClientFactoryBean implements FactoryBean<RedisClient> {
     7     private RedisClientConfig redisClientConfig = new RedisClientConfig();
     8 
     9     public RedisClient getObject() throws Exception {
    10         if (StringUtils.isNotBlank(this.redisClientConfig.getIpConfString())) {
    11             return new RedisClient(this.redisClientConfig);
    12         }
    13         throw new RedisInitializerException("RedisClient init parameter masterConfString is empty,please check spring config file!");
    14     }
    15 
    16     public Class<?> getObjectType() {
    17         return RedisClient.class;
    18     }
    19 
    20     public boolean isSingleton() {
    21         return true;
    22     }
    23 
    24     public void setIpConfString(String string) {
    25         this.redisClientConfig.setIpConfString(string);
    26     }
    27 
    28     public void setMaxTotal(int maxTotal) {
    29         this.redisClientConfig.setMaxTotal(maxTotal);
    30     }
    31 
    32     public void setMaxIdle(int maxIdle) {
    33         this.redisClientConfig.setMaxIdle(maxIdle);
    34     }
    35 
    36     public void setMinIdle(int minIdle) {
    37         this.redisClientConfig.setMinIdle(minIdle);
    38     }
    39 
    40     public void setTestOnBorrow(boolean flag) {
    41         this.redisClientConfig.setTestOnBorrow(flag);
    42     }
    43 }
    View Code

    10.4 RedisClientConfig.java

     1 package com.commons.redis;
     2 
     3 import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
     4 
     5 /**
     6  * 
     7  * @描述 : 配置redis的相关属性
     8  * @创建时间: 2015年7月20日上午11:05:53
     9  *
    10  */
    11 public class RedisClientConfig {
    12     private GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();
    13     
    14     private String ipConfString;
    15 
    16     /**
    17      * 
    18      * @描述 : 单个应用中的链接池最大链接数
    19      * @创建时间: 2015年7月20日上午11:11:10
    20      *
    21      * @param maxTotal
    22      */
    23     public void setMaxTotal(int maxTotal) {
    24         this.jedisPoolConfig.setMaxTotal(maxTotal);
    25     }
    26 
    27     /**
    28      * @描述 :单个应用中的链接池最大空闲数 
    29      * @创建时间: 2015年7月20日上午11:11:28
    30      *
    31      * @param maxIdle
    32      */
    33     
    34     
    35     public void setMaxIdle(int maxIdle) {
    36         this.jedisPoolConfig.setMaxIdle(maxIdle);
    37     }
    38 
    39     
    40     /**
    41      * @描述 : 单个应用中的链接池最大链接数
    42      * @创建时间: 2015年7月20日上午11:11:43
    43      *
    44      * @param maxIdle
    45      */
    46     public void setMinIdle(int minIdle) {
    47         this.jedisPoolConfig.setMinIdle(minIdle);
    48     }
    49 
    50     /**
    51      * @描述 : 设置在每一次取对象时测试ping 
    52      * @创建时间: 2015年7月20日上午11:11:58
    53      *
    54      * @param flag
    55      */
    56     public void setTestOnBorrow(boolean flag) {
    57         this.jedisPoolConfig.setTestOnBorrow(flag);
    58     }
    59 
    60     public GenericObjectPoolConfig getJedisPoolConfig() {
    61         return this.jedisPoolConfig;
    62     }
    63 
    64     public String getIpConfString() {
    65         return this.ipConfString;
    66     }
    67 
    68     public void setIpConfString(String ipConfString) {
    69         this.ipConfString = ipConfString;
    70     }
    71 }
    View Code

    10.5 RedisClient.java

      1 package com.commons.redis;
      2 
      3 import java.util.List;
      4 import java.util.Map;
      5 import java.util.Set;
      6 
      7 import org.apache.commons.lang3.StringUtils;
      8 import org.apache.commons.logging.Log;
      9 import org.apache.commons.logging.LogFactory;
     10 
     11 import redis.clients.jedis.BinaryClient;
     12 import redis.clients.jedis.Jedis;
     13 import redis.clients.jedis.JedisPool;
     14 
     15 public class RedisClient {
     16     private static final Log LOG = LogFactory.getLog(RedisClient.class);
     17     private RedisClientConfig redisClientConfig;
     18     private JedisPool jedisPool;
     19 
     20     public RedisClient(RedisClientConfig redisClientConfig) {
     21         this.redisClientConfig = redisClientConfig;
     22         init();
     23     }
     24 
     25     /**
     26      * 
     27      * @描述 : 初始化,配置ip和端口
     28      * @创建时间: 2015年7月20日下午1:43:29
     29      *
     30      */
     31     private void init() {
     32         LOG.info("redis client init start~");
     33         String ip = "";
     34         int port;
     35         if (StringUtils.isNotBlank(this.redisClientConfig.getIpConfString())) {
     36             String[] ipPortArray = this.redisClientConfig.getIpConfString().split(":");
     37             if (ipPortArray.length == 1) {
     38                 throw new RedisInitializerException(ipPortArray + " is not include host:port or host:port after split ":"");
     39             }
     40             ip = ipPortArray[0];
     41             port = Integer.valueOf(ipPortArray[1]).intValue();
     42         } else {
     43             throw new RuntimeException("init throw exception");
     44         }
     45         LOG.info("write redis client connect ip:" + ip + ",port:" + port);
     46         this.jedisPool = new JedisPool(this.redisClientConfig.getJedisPoolConfig(), ip, port);
     47     }
     48 
     49     /**
     50      * 
     51      * @描述 : 设置键值对
     52      * @创建时间: 2015年7月20日下午1:44:10
     53      *
     54      * @param key
     55      * @param value
     56      * @return
     57      * @throws RedisAccessException
     58      */
     59     public String set(String key, String value) throws RedisAccessException {
     60         Jedis client = null;
     61         try {
     62             client = jedisPool.getResource();
     63             return client.set(key, value);
     64         } catch (Exception e) {
     65             LOG.error(e.getMessage(), e);
     66             throw new RedisAccessException(e);
     67         } finally {
     68             if (null != client) {
     69                 jedisPool.returnResourceObject(client);
     70             }
     71         }
     72     }
     73 
     74     /**
     75      * 
     76      * @描述 : 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX
     77      *     命令将覆写旧值。
     78      * @创建时间: 2015年7月20日下午1:44:36
     79      *
     80      * @param key
     81      * @param seconds
     82      * @param value
     83      * @return
     84      * @throws RedisAccessException
     85      */
     86     public String set(String key, int seconds, String value) throws RedisAccessException {
     87         Jedis client = null;
     88         try {
     89             client = jedisPool.getResource();
     90             return client.setex(key, seconds, value);
     91         } catch (Exception e) {
     92             LOG.error(e.getMessage(), e);
     93             throw new RedisAccessException(e);
     94         } finally {
     95             if (null != client) {
     96                 jedisPool.returnResourceObject(client);
     97             }
     98         }
     99     }
    100 
    101     /**
    102      * 
    103      * @描述 : 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 如果 key 不存在,
    104      *     APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
    105      * @创建时间: 2015年7月20日下午1:45:55
    106      *
    107      * @param key
    108      * @param value
    109      * @return
    110      * @throws RedisAccessException
    111      */
    112     public Long append(String key, String value) throws RedisAccessException {
    113         Jedis client = null;
    114         try {
    115             client = jedisPool.getResource();
    116             return client.append(key, value);
    117         } catch (Exception e) {
    118             LOG.error(e.getMessage(), e);
    119             throw new RedisAccessException(e);
    120         } finally {
    121             if (null != client) {
    122                 jedisPool.returnResourceObject(client);
    123             }
    124         }
    125     }
    126 
    127     /**
    128      * 
    129      * @描述 : 返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 null 。 假如 key
    130      *     储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值。
    131      * @创建时间: 2015年7月20日下午1:47:10
    132      *
    133      * @param key
    134      * @return
    135      * @throws RedisAccessException
    136      */
    137     public String get(String key) throws RedisAccessException {
    138         Jedis client = null;
    139         try {
    140             client = jedisPool.getResource();
    141             return client.get(key);
    142         } catch (Exception e) {
    143             LOG.error(e.getMessage(), e);
    144             throw new RedisAccessException(e);
    145         } finally {
    146             if (null != client) {
    147                 jedisPool.returnResourceObject(client);
    148             }
    149         }
    150     }
    151 
    152     /**
    153      * 
    154      * @描述 : 删除给定的一个或多个 key 。 不存在的 key 会被忽略。
    155      * @创建时间: 2015年7月20日下午1:48:37
    156      *
    157      * @param key
    158      * @return
    159      * @throws RedisAccessException
    160      */
    161     public Long del(String key) throws RedisAccessException {
    162         Jedis client = null;
    163         try {
    164             client = jedisPool.getResource();
    165             return client.del(key);
    166         } catch (Exception e) {
    167             LOG.error(e.getMessage(), e);
    168             throw new RedisAccessException(e);
    169         } finally {
    170             if (null != client) {
    171                 jedisPool.returnResourceObject(client);
    172             }
    173         }
    174     }
    175 
    176     /**
    177      * 
    178      * @描述 : 检查给定 key 是否存在。
    179      * @创建时间: 2015年7月20日下午1:48:48
    180      *
    181      * @param key
    182      * @return
    183      * @throws RedisAccessException
    184      */
    185     public Boolean exists(String key) throws RedisAccessException {
    186         Jedis client = null;
    187         try {
    188             client = jedisPool.getResource();
    189             return client.exists(key);
    190         } catch (Exception e) {
    191             LOG.error(e.getMessage(), e);
    192             throw new RedisAccessException(e);
    193         } finally {
    194             if (null != client) {
    195                 jedisPool.returnResourceObject(client);
    196             }
    197         }
    198     }
    199 
    200     /**
    201      * @描述 : 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
    202      * @创建时间: 2015年7月20日下午1:49:29
    203      *
    204      * @param key
    205      * @param seconds
    206      * @return
    207      * @throws RedisAccessException
    208      */
    209     public Long expire(String key, int seconds) throws RedisAccessException {
    210         Jedis client = null;
    211         try {
    212             client = jedisPool.getResource();
    213             return client.expire(key, seconds);
    214         } catch (Exception e) {
    215             LOG.error(e.getMessage(), e);
    216             throw new RedisAccessException(e);
    217         } finally {
    218             if (null != client) {
    219                 jedisPool.returnResourceObject(client);
    220             }
    221         }
    222     }
    223     
    224     
    225     
    226     /**
    227      * @描述 : 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
    228      * @创建时间: 2015年7月20日下午1:49:29
    229      *
    230      * @param key
    231      * @param seconds
    232      * @return
    233      * @throws RedisAccessException
    234      */
    235     public Long expireAt(String key, long unixTime) throws RedisAccessException {
    236         Jedis client = null;
    237         try {
    238             client = jedisPool.getResource();
    239             return client.expireAt(key,unixTime);
    240         } catch (Exception e) {
    241             LOG.error(e.getMessage(), e);
    242             throw new RedisAccessException(e);
    243         } finally {
    244             if (null != client) {
    245                 jedisPool.returnResourceObject(client);
    246             }
    247         }
    248     }
    249 
    250     /**
    251      * 
    252      * @描述 : 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key
    253      *     存在但不是字符串类型时,返回一个错误。
    254      * @创建时间: 2015年7月20日下午1:50:28
    255      *
    256      * @param key
    257      * @param value
    258      * @return
    259      * @throws RedisAccessException
    260      */
    261     public String getSet(String key, String value) throws RedisAccessException {
    262         Jedis client = null;
    263         try {
    264             client = jedisPool.getResource();
    265             return client.getSet(key, value);
    266         } catch (Exception e) {
    267             LOG.error(e.getMessage(), e);
    268             throw new RedisAccessException(e);
    269         } finally {
    270             if (null != client) {
    271                 jedisPool.returnResourceObject(client);
    272             }
    273         }
    274     }
    275 
    276     /**
    277      * 
    278      * @描述 : 将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。
    279      * @创建时间: 2015年7月20日下午1:52:07
    280      *
    281      * @param key
    282      * @param value
    283      * @return
    284      * @throws RedisAccessException
    285      */
    286     public Long setnx(String key, String value) throws RedisAccessException {
    287         Jedis client = null;
    288         try {
    289             client = jedisPool.getResource();
    290             return client.setnx(key, value);
    291         } catch (Exception e) {
    292             LOG.error(e.getMessage(), e);
    293             throw new RedisAccessException(e);
    294         } finally {
    295             if (null != client) {
    296                 jedisPool.returnResourceObject(client);
    297             }
    298         }
    299     }
    300 
    301     /**
    302      * 
    303      * @描述 : 将哈希表 key 中的域 field 的值设为 value 。 如果 key 不存在,一个新的哈希表被创建并进行 HSET操作。
    304      *     如果域 field 已经存在于哈希表中,旧值将被覆盖。
    305      * @创建时间: 2015年7月20日下午1:53:23
    306      *
    307      * @param key
    308      * @param field
    309      * @param value
    310      * @return
    311      * @throws RedisAccessException
    312      */
    313     public Long hset(String key, String field, String value) throws RedisAccessException {
    314         Jedis client = null;
    315         try {
    316             client = jedisPool.getResource();
    317             return client.hset(key, field, value);
    318         } catch (Exception e) {
    319             LOG.error(e.getMessage(), e);
    320             throw new RedisAccessException(e);
    321         } finally {
    322             if (null != client) {
    323                 jedisPool.returnResourceObject(client);
    324             }
    325         }
    326     }
    327 
    328     /**
    329      * 
    330      * @描述 :返回哈希表 key 中给定域 field 的值。
    331      * @创建时间: 2015年7月20日下午1:54:24
    332      *
    333      * @param key
    334      * @param field
    335      * @return
    336      * @throws RedisAccessException
    337      */
    338     public String hget(String key, String field) throws RedisAccessException {
    339         Jedis client = null;
    340         try {
    341             client = jedisPool.getResource();
    342             return client.hget(key, field);
    343         } catch (Exception e) {
    344             LOG.error(e.getMessage(), e);
    345             throw new RedisAccessException(e);
    346         } finally {
    347             if (null != client) {
    348                 jedisPool.returnResourceObject(client);
    349             }
    350         }
    351     }
    352 
    353     /**
    354      * 
    355      * @描述 : 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。若域 field
    356      *     已经存在,该操作无效。 如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令。
    357      * @创建时间: 2015年7月20日下午1:54:44
    358      *
    359      * @param key
    360      * @param field
    361      * @param value
    362      * @return
    363      * @throws RedisAccessException
    364      */
    365     public Long hsetnx(String key, String field, String value) throws RedisAccessException {
    366         Jedis client = null;
    367         try {
    368             client = jedisPool.getResource();
    369             return client.hsetnx(key, field, value);
    370         } catch (Exception e) {
    371             LOG.error(e.getMessage(), e);
    372             throw new RedisAccessException(e);
    373         } finally {
    374             if (null != client) {
    375                 jedisPool.returnResourceObject(client);
    376             }
    377         }
    378     }
    379 
    380     /**
    381      * 
    382      * @描述 :同时将多个 field-value (域-值)对设置到哈希表 key 中。此命令会覆盖哈希表中已存在的域。 如果 key
    383      *     不存在,一个空哈希表被创建并执行 HMSET 操作。
    384      * @创建时间: 2015年7月20日下午1:58:26
    385      *
    386      * @param key
    387      * @param hash
    388      * @return
    389      * @throws RedisAccessException
    390      */
    391     public String hmset(String key, Map<String, String> hash) throws RedisAccessException {
    392         Jedis client = null;
    393         try {
    394             client = jedisPool.getResource();
    395             return client.hmset(key, hash);
    396         } catch (Exception e) {
    397             LOG.error(e.getMessage(), e);
    398             throw new RedisAccessException(e);
    399         } finally {
    400             if (null != client) {
    401                 jedisPool.returnResourceObject(client);
    402             }
    403         }
    404     }
    405 
    406     /**
    407      * 
    408      * @描述 : 返回哈希表 key 中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个 null 值。 因为不存在的 key
    409      *     被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 null 值的表。
    410      * @创建时间: 2015年7月20日下午1:57:32
    411      *
    412      * @param key
    413      * @param fields
    414      * @return
    415      * @throws RedisAccessException
    416      */
    417     public List<String> hmget(String key, String[] fields) throws RedisAccessException {
    418         Jedis client = null;
    419         try {
    420             client = jedisPool.getResource();
    421             return client.hmget(key, fields);
    422         } catch (Exception e) {
    423             LOG.error(e.getMessage(), e);
    424             throw new RedisAccessException(e);
    425         } finally {
    426             if (null != client) {
    427                 jedisPool.returnResourceObject(client);
    428             }
    429         }
    430     }
    431 
    432     /**
    433      * 
    434      * @描述 :删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
    435      * @创建时间: 2015年7月20日下午1:59:32
    436      *
    437      * @param key
    438      * @param fields
    439      * @return
    440      * @throws RedisAccessException
    441      */
    442     public Long hdel(String key, String[] fields) throws RedisAccessException {
    443         Jedis client = null;
    444         try {
    445             client = jedisPool.getResource();
    446             return client.hdel(key, fields);
    447         } catch (Exception e) {
    448             LOG.error(e.getMessage(), e);
    449             throw new RedisAccessException(e);
    450         } finally {
    451             if (null != client) {
    452                 jedisPool.returnResourceObject(client);
    453             }
    454         }
    455     }
    456 
    457     /**
    458      * 
    459      * @描述 : 返回哈希表 key 中域的数量。
    460      * @创建时间: 2015年7月20日下午2:00:13
    461      *
    462      * @param key
    463      * @return
    464      * @throws RedisAccessException
    465      */
    466     public Long hlen(String key) throws RedisAccessException {
    467         Jedis client = null;
    468         try {
    469             client = jedisPool.getResource();
    470             return client.hlen(key);
    471         } catch (Exception e) {
    472             LOG.error(e.getMessage(), e);
    473             throw new RedisAccessException(e);
    474         } finally {
    475             if (null != client) {
    476                 jedisPool.returnResourceObject(client);
    477             }
    478         }
    479     }
    480 
    481     /**
    482      * 
    483      * @描述 :返回哈希表 key 中的所有域。
    484      * @创建时间: 2015年7月20日下午2:00:59
    485      *
    486      * @param key
    487      * @return
    488      * @throws RedisAccessException
    489      */
    490     public Set<String> hkeys(String key) throws RedisAccessException {
    491         Jedis client = null;
    492         try {
    493             client = jedisPool.getResource();
    494             return client.hkeys(key);
    495         } catch (Exception e) {
    496             LOG.error(e.getMessage(), e);
    497             throw new RedisAccessException(e);
    498         } finally {
    499             if (null != client) {
    500                 jedisPool.returnResourceObject(client);
    501             }
    502         }
    503     }
    504 
    505     /**
    506      * 
    507      * @描述 : 返回哈希表 key 中所有域的值。
    508      * @创建时间: 2015年7月20日下午2:01:06
    509      *
    510      * @param key
    511      * @return
    512      * @throws RedisAccessException
    513      */
    514     public List<String> hvals(String key) throws RedisAccessException {
    515         Jedis client = null;
    516         try {
    517             client = jedisPool.getResource();
    518             return client.hvals(key);
    519         } catch (Exception e) {
    520             LOG.error(e.getMessage(), e);
    521             throw new RedisAccessException(e);
    522         } finally {
    523             if (null != client) {
    524                 jedisPool.returnResourceObject(client);
    525             }
    526         }
    527     }
    528 
    529     /**
    530      * 
    531      * @描述 : 返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名(field
    532      *     name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
    533      * @创建时间: 2015年7月20日下午2:01:36
    534      *
    535      * @param key
    536      * @return
    537      * @throws RedisAccessException
    538      */
    539     public Map<String, String> hgetAll(String key) throws RedisAccessException {
    540         Jedis client = null;
    541         try {
    542             client = jedisPool.getResource();
    543             return client.hgetAll(key);
    544         } catch (Exception e) {
    545             LOG.error(e.getMessage(), e);
    546             throw new RedisAccessException(e);
    547         } finally {
    548             if (null != client) {
    549                 jedisPool.returnResourceObject(client);
    550             }
    551         }
    552     }
    553 
    554     /**
    555      * 
    556      * @描述 :查看哈希表 key 中,给定域 field 是否存在。
    557      * @创建时间: 2015年7月20日下午2:02:38
    558      *
    559      * @param key
    560      * @param field
    561      * @return
    562      * @throws RedisAccessException
    563      */
    564     public Boolean hexists(String key, String field) throws RedisAccessException {
    565         Jedis client = null;
    566         try {
    567             client = jedisPool.getResource();
    568             return client.hexists(key, field);
    569         } catch (Exception e) {
    570             LOG.error(e.getMessage(), e);
    571             throw new RedisAccessException(e);
    572         } finally {
    573             if (null != client) {
    574                 jedisPool.returnResourceObject(client);
    575             }
    576         }
    577     }
    578 
    579     /**
    580      * 
    581      * @描述 : 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
    582      * @创建时间: 2015年7月20日下午2:02:44
    583      *
    584      * @param key
    585      * @param values
    586      * @return
    587      * @throws RedisAccessException
    588      */
    589     public Long rpush(String key, String[] values) throws RedisAccessException {
    590         Jedis client = null;
    591         try {
    592             client = jedisPool.getResource();
    593             return client.rpush(key, values);
    594         } catch (Exception e) {
    595             LOG.error(e.getMessage(), e);
    596             throw new RedisAccessException(e);
    597         } finally {
    598             if (null != client) {
    599                 jedisPool.returnResourceObject(client);
    600             }
    601         }
    602     }
    603 
    604     /**
    605      * 
    606      * @描述 : 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
    607      * @创建时间: 2015年7月20日下午2:03:20
    608      *
    609      * @param key
    610      * @param value
    611      * @return
    612      * @throws RedisAccessException
    613      */
    614     public Long rpushx(String key, String value) throws RedisAccessException {
    615         Jedis client = null;
    616         try {
    617             client = jedisPool.getResource();
    618             return client.rpushx(key, new String[] { value });
    619         } catch (Exception e) {
    620             LOG.error(e.getMessage(), e);
    621             throw new RedisAccessException(e);
    622         } finally {
    623             if (null != client) {
    624                 jedisPool.returnResourceObject(client);
    625             }
    626         }
    627     }
    628 
    629     /**
    630      * 
    631      * @描述 : 将一个或多个值 value 插入到列表 key 的表头
    632      * @创建时间: 2015年7月20日下午2:04:13
    633      *
    634      * @param key
    635      * @param values
    636      * @return
    637      * @throws RedisAccessException
    638      */
    639     public Long lpush(String key, String[] values) throws RedisAccessException {
    640         Jedis client = null;
    641         try {
    642             client = jedisPool.getResource();
    643             return client.lpush(key, values);
    644         } catch (Exception e) {
    645             LOG.error(e.getMessage(), e);
    646             throw new RedisAccessException(e);
    647         } finally {
    648             if (null != client) {
    649                 jedisPool.returnResourceObject(client);
    650             }
    651         }
    652     }
    653 
    654     /**
    655      * 
    656      * @描述 : 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。 和 LPUSH 命令相反,当 key 不存在时,
    657      *     LPUSHX 命令什么也不做。
    658      * @创建时间: 2015年7月20日下午2:05:09
    659      *
    660      * @param key
    661      * @param value
    662      * @return
    663      * @throws RedisAccessException
    664      */
    665     public Long lpushx(String key, String value) throws RedisAccessException {
    666         Jedis client = null;
    667         try {
    668             client = jedisPool.getResource();
    669             return client.lpushx(key, new String[] { value });
    670         } catch (Exception e) {
    671             LOG.error(e.getMessage(), e);
    672             throw new RedisAccessException(e);
    673         } finally {
    674             if (null != client) {
    675                 jedisPool.returnResourceObject(client);
    676             }
    677         }
    678     }
    679 
    680     /**
    681      * 
    682      * @描述 : 返回列表 key 的长度。如果 key 不存在,则 key 被解释为一个空列表,返回 0 . 如果 key
    683      *     不是列表类型,返回一个错误。
    684      * @创建时间: 2015年7月20日下午2:05:25
    685      *
    686      * @param key
    687      * @return
    688      * @throws RedisAccessException
    689      */
    690     public Long llen(String key) throws RedisAccessException {
    691         Jedis client = null;
    692         try {
    693             client = jedisPool.getResource();
    694             return client.llen(key);
    695         } catch (Exception e) {
    696             LOG.error(e.getMessage(), e);
    697             throw new RedisAccessException(e);
    698         } finally {
    699             if (null != client) {
    700                 jedisPool.returnResourceObject(client);
    701             }
    702         }
    703     }
    704 
    705     /**
    706      * 
    707      * @描述 : 返回列表 key 中,下标为 index 的元素。
    708      * @创建时间: 2015年7月20日下午2:05:37
    709      *
    710      * @param key
    711      * @param index
    712      * @return
    713      * @throws RedisAccessException
    714      */
    715     public String lindex(String key, long index) throws RedisAccessException {
    716         Jedis client = null;
    717         try {
    718             client = jedisPool.getResource();
    719             return client.lindex(key, index);
    720         } catch (Exception e) {
    721             LOG.error(e.getMessage(), e);
    722             throw new RedisAccessException(e);
    723         } finally {
    724             if (null != client) {
    725                 jedisPool.returnResourceObject(client);
    726             }
    727         }
    728     }
    729 
    730     /**
    731      * 
    732      * @描述 : 将列表 key 下标为 index 的元素的值设置为 value 。当 index 参数超出范围,或对一个空列表( key
    733      *     不存在)进行 LSET 时,返回一个错误。
    734      * @创建时间: 2015年7月20日下午2:07:06
    735      *
    736      * @param key
    737      * @param index
    738      * @param value
    739      * @return
    740      * @throws RedisAccessException
    741      */
    742     public String lset(String key, long index, String value) throws RedisAccessException {
    743         Jedis client = null;
    744         try {
    745             client = jedisPool.getResource();
    746             return client.lset(key, index, value);
    747         } catch (Exception e) {
    748             LOG.error(e.getMessage(), e);
    749             throw new RedisAccessException(e);
    750         } finally {
    751             if (null != client) {
    752                 jedisPool.returnResourceObject(client);
    753             }
    754         }
    755     }
    756 
    757     /**
    758      * 
    759      * @描述 : 移除并返回列表 key 的头元素。
    760      * @创建时间: 2015年7月20日下午2:07:10
    761      *
    762      * @param key
    763      * @return
    764      * @throws RedisAccessException
    765      */
    766     public String lpop(String key) throws RedisAccessException {
    767         Jedis client = null;
    768         try {
    769             client = jedisPool.getResource();
    770             return client.lpop(key);
    771         } catch (Exception e) {
    772             LOG.error(e.getMessage(), e);
    773             throw new RedisAccessException(e);
    774         } finally {
    775             if (null != client) {
    776                 jedisPool.returnResourceObject(client);
    777             }
    778         }
    779     }
    780 
    781     /**
    782      * 
    783      * @描述 : 移除并返回列表 key 的尾元素。
    784      * @创建时间: 2015年7月20日下午2:07:13
    785      *
    786      * @param key
    787      * @return
    788      * @throws RedisAccessException
    789      */
    790     public String rpop(String key) throws RedisAccessException {
    791         Jedis client = null;
    792         try {
    793             client = jedisPool.getResource();
    794             return client.rpop(key);
    795         } catch (Exception e) {
    796             LOG.error(e.getMessage(), e);
    797             throw new RedisAccessException(e);
    798         } finally {
    799             if (null != client) {
    800                 jedisPool.returnResourceObject(client);
    801             }
    802         }
    803     }
    804 
    805     /**
    806      * 
    807      * @描述 : 将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。当 pivot 不存在于列表 key 时,不执行任何操作。
    808      *     key 不存在时, key 被视为空列表,不执行任何操作。 如果 key 不是列表类型,返回一个错误。
    809      * @创建时间: 2015年7月20日下午2:07:37
    810      *
    811      * @param key
    812      * @param where
    813      * @param pivot
    814      * @param value
    815      * @return
    816      * @throws RedisAccessException
    817      */
    818     public Long linsert(String key, BinaryClient.LIST_POSITION where, String pivot, String value) throws RedisAccessException {
    819         Jedis client = null;
    820         try {
    821             client = jedisPool.getResource();
    822             return client.linsert(key, where, pivot, value);
    823         } catch (Exception e) {
    824             LOG.error(e.getMessage(), e);
    825             throw new RedisAccessException(e);
    826         } finally {
    827             if (null != client) {
    828                 jedisPool.returnResourceObject(client);
    829             }
    830         }
    831     }
    832     
    833     /**
    834      * @描述 : 将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
    835      * @创建时间: 2014-6-16下午2:31:58
    836      * 
    837      * @param key
    838      * @return
    839      * @throws RedisAccessException
    840      */
    841     public Long incr(String key) throws RedisAccessException {
    842         Jedis client = null;
    843         try {
    844             client = jedisPool.getResource();
    845             return client.incr(key);
    846         } catch (Exception e) {
    847             LOG.error(e.getMessage(), e);
    848             throw new RedisAccessException(e);
    849         } finally {
    850             if (null != client) {
    851                 jedisPool.returnResourceObject(client);
    852             }
    853         }
    854     }
    855     
    856     
    857     /**
    858      * @描述 :  将 key 所储存的值加上增量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行  INCRBY 命令。
    859      * @创建时间: 2014-6-16下午2:31:58
    860      * 
    861      * @param key
    862      * @return
    863      * @throws RedisAccessException
    864      */
    865     public double incrBy(String key,long value) throws RedisAccessException {
    866         Jedis client = null;
    867         try {
    868             client = jedisPool.getResource();
    869             return client.incrBy(key, value);
    870         } catch (Exception e) {
    871             LOG.error(e.getMessage(), e);
    872             throw new RedisAccessException(e);
    873         } finally {
    874             if (null != client) {
    875                 jedisPool.returnResourceObject(client);
    876             }
    877         }
    878     }
    879 
    880 }
    View Code

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

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

  • 相关阅读:
    Shell 脚本中调用另一个 Shell 脚本的三种方式
    Shell脚本的三种执行方式
    python selenium中Excel数据维护(二)
    python里面的xlrd模块详解(一)
    selenium处理iframe定位于切换问题解决办法
    Selenium在定位的class含有空格的复合类的解决办法整理
    SqlSugar ORM已经支持读写分离
    ASP.NET Core的Kestrel服务器
    服务是如何加载并运行的, Kestrel、配置与环境
    ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解
  • 原文地址:https://www.cnblogs.com/gzy-blog/p/6058849.html
Copyright © 2011-2022 走看看