学习Redis5这一篇就够了


配套资料,免费下载
链接:https://pan.baidu.com/s/1Y8gVdeZrG-UQutHdyJLHOw
提取码:zgus
复制这段内容后打开百度网盘手机App,操作更方便哦

第一章 Redis概述

1.1、Redis介绍

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。Redis是 NoSQL技术阵营中的一员,它通过多种键值数据类型来适应不同场景下的存储需求,借助一些高层级的接口使其可以胜任如缓存、队列系统的不同角色

1.2、Redis特性

  • Redis特点:
    • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供list、set、zset、hash等数据结构的存储
    • Redis支持数据的备份,即master-slave模式的数据备份
  • Redis优势:
    • 性能极高:Redis能读的速度是110000次/s,写的速度是81000次/s
    • 丰富的数据类型:Redis支持二进制案例的Strings、Lists、Hashes、Sets及Ordered Sets数据类型操作
    • 原子性:Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行
    • 丰富的特性:Redis还支持publish/subscribe、通知、key 过期等等特性

1.3、Redis官网

官方网址:https://redis.io/

第二章 Redis下载及安装

2.1、准备编译器

[root@caochenlei ~]# yum -y install gcc gcc-c++

2.2、下载并安装

[root@caochenlei ~]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz
[root@caochenlei ~]# tar -zxvf redis-5.0.9.tar.gz
[root@caochenlei ~]# cd redis-5.0.9
[root@caochenlei redis-5.0.9]# make
[root@caochenlei redis-5.0.9]# make PREFIX=/usr/local/redis install
[root@caochenlei redis-5.0.9]# cp redis.conf /usr/local/redis

2.3、启动服务器

[root@caochenlei redis-5.0.9]# cd /usr/local/redis
[root@caochenlei redis]# vim redis.conf
#找到这个修改为yes,表示Redis启动的时候采用后台模式
daemonize yes
[root@caochenlei redis]# ./bin/redis-server redis.conf

2.4、启动客户端

[root@caochenlei redis]# ./bin/redis-cli

2.5、文件的介绍

  • redis-server :Redis服务器
  • redis-cli :Redis客户端
  • redis-sentinel :Redis哨兵端
  • redis-benchmark:Redis性能测试工具
  • redis-check-aof :AOF文件修复工具
  • redis-check-rdb :RDB文件检索工具

第三章 Redis类型及操作

3.1、String类型及常见操作

String类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等,在Redis中字符串类型的Value最多可以容纳的数据长度是512M。

  • SET key value

设定该Key持有指定的字符串Value,如果该Key已经存在,则覆盖其原有值。

返回值:该命令不会失败,始终返回OK。

  • GET key

获取指定Key的Value。如果与该Key关联的Value不是String类型,Redis将返回错误信息,因为GET命令只能用于获取String Value。

返回值:与该Key相关的Value,如果该Key不存在,则返回nil。

  • MSET key value [key value …]

该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行SET命令。

返回值:该命令不会失败,始终返回OK。

  • MGET key [key …]

返回所有指定Keys的Values,如果其中某个Key不存在,或者其值不为String类型,该Key的Value将返回nil。

返回值:返回一组指定Keys的Values的列表。

  • SETNX key value

如果指定的Key不存在,则设定该Key持有指定字符串Value,此时其效果等价于SET命令。相反,如果该Key已经存在,该命令将不做任何操作并返回。

返回值:1表示设置成功,否则0。

  • MSETNX key value [key value …]

该命令原子性的完成参数中所有key/value的设置操作,其具体行为可以看成是多次迭代执行SETNX命令。然而这里需要明确说明的是,如果在这一批Keys中有任意一个Key已经存在了,那么该操作将全部回滚,即所有的修改都不会生效。

返回值:1表示所有Keys都设置成功,0则表示没有任何Key被修改。

  • APPEND key value

如果该Key已经存在,APPEND命令将参数Value的数据追加到已存在Value的末尾。如果该Key不存在,APPEND命令将会创建一个新的Key/Value。

返回值:追加后Value的长度。

  • DECR key

将指定Key的Value原子性的递减1。如果该Key不存在,其初始值为0,在decr之后其值为-1。如果Value的值不能转换为整型值,如Hello,该操作将执行失败并返回相应的错误信息。

返回值:递减后的Value值。

  • INCR key

将指定Key的Value原子性的递增1。如果该Key不存在,其初始值为0,在incr之后其值为1。如果Value的值不能转换为整型值,如Hello,该操作将执行失败并返回相应的错误信息。

返回值:递增后的Value值。

  • DECRBY key decrement

将指定Key的Value原子性的减少decrement。如果该Key不存在,其初始值为0,在decrby之后其值为-decrement。如果Value的值不能转换为整型值,如Hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型。

返回值:减少后的Value值。

  • INCRBY key increment

将指定Key的Value原子性的增加increment。如果该Key不存在,其初始值为0,在incrby之后其值为increment。如果Value的值不能转换为整型值,如Hello,该操作将执行失败并返回相应的错误信息。注意:该操作的取值范围是64位有符号整型。

返回值:增加后的Value值。

  • GETSET key value

原子性的设置该Key为指定的Value,同时返回该Key的原有值。和GET命令一样,该命令也只能处理String Value,否则Redis将给出相关的错误信息。

返回值:返回该Key的原有值,如果该Key之前并不存在,则返回nil。

  • STRLEN key

返回指定Key的字符值长度,如果Value不是String类型,Redis将执行失败并给出相关的错误信息。

返回值:指定Key的Value字符长度,如果该Key不存在,返回0。

  • SETEX key seconds value

原子性完成两个操作,一是设置该Key的值为指定字符串,同时设置该Key在Redis服务器中的存活时间(秒数)。该命令主要应用于Redis被当做Cache服务器使用时。

返回值:操作成功返回OK。

  • SETRANGE key offset value

替换指定Key的部分字符串值。从offset开始,替换的长度为该命令第三个参数value的字符串长度,其中如果offset的值大于该Key的原有值Value的字符串长度,Redis将会在Value的后面补齐(offset - strlen(value))数量的0x00,之后再追加新值。如果该键不存在,该命令会将其原值的长度假设为0,并在其后添补offset个0x00后再追加新值。鉴于字符串Value的最大长度为512M,因此offset的最大值为536870911。最后需要注意的是,如果该命令在执行时致使指定Key的原有值长度增加,这将会导致Redis重新分配足够的内存以容纳替换后的全部字符串,因此就会带来一定的性能折损。

返回值:修改后的字符串Value长度。

  • GETRANGE key start end

截取字符串。该命令在截取子字符串时,将以闭区间的方式同时包含start(0表示第一个字符)和end所在的字符,如果end值超过Value的字符长度,该命令将只是截取从start开始之后所有的字符数据。

返回值:子字符串。

  • SETBIT key offset value

设置在指定Offset上BIT的值,该值只能为1或0,在设定后该命令返回该Offset上原有的BIT值。如果指定Key不存在,该命令将创建一个新值,并在指定的Offset上设定参数中的BIT值。如果Offset大于Value的字符长度,Redis将拉长Value值并在指定Offset上设置参数中的BIT值,中间添加的BIT值为0。最后需要说明的是Offset值必须大于0。

返回值:在指定Offset上的BIT原有值。

  • GETBIT key offset

返回在指定Offset上BIT的值,0或1。如果Offset超过String value的长度,该命令将返回0,所以对于空字符串始终返回0。

返回值:在指定Offset上的BIT值。

3.2、List类型及常见操作

在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。

  • LPUSH key value [value …]

在指定Key所关联的List Value的头部插入参数中给出的所有Values。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的头部插入。如果该键的Value不是链表类型,该命令将返回相关的错误信息。

返回值:插入后链表中元素的数量。

  • LPUSHX key value

仅有当参数中指定的Key存在时,该命令才会在其所关联的List Value的头部插入参数中给出的Value,否则将不会有任何操作发生。

返回值:插入后链表中元素的数量。

  • LRANGE key start stop

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

返回值:返回指定范围内元素的列表。

  • LPOP key

返回并弹出指定Key关联的链表中的第一个元素,即头部元素。如果该Key不存,返回nil。

返回值:链表头部的元素。

  • LLEN key

返回指定Key关联的链表中元素的数量,如果该Key不存在,则返回0。如果与该Key关联的Value的类型不是链表,则返回相关的错误信息。

返回值:链表中元素的数量。

  • LREM key count value

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

返回值:返回被删除的元素数量。

  • LSET key index value

设定链表中指定位置的值为新值,其中0表示第一个元素,即头部元素,-1表示尾部元素。如果索引值Index超出了链表中元素的数量范围,该命令将返回相关的错误信息。

返回值:操作成功返回OK。

  • LINDEX key index

该命令将返回链表中指定位置(index)的元素,index是以0开始的,即0表示链表头部的第一个元素,如果index为-1,表示尾部元素。如果与该Key关联的不是链表,该命令将返回相关的错误信息。

返回值:返回请求的元素,如果index超出范围,则返回nil。

  • LTRIM key start stop

该命令将仅保留指定范围内的元素,从而保证链接中的元素数量相对恒定。start和stop参数都是以0开始的,即0表示链表头部的第一个元素。和其他命令一样,start和stop也可以为负值,-1表示尾部元素。如果start大于链表的尾部,或start大于stop,该命令不会报错,而是返回一个空的链表,与此同时该Key也将被删除。如果stop大于元素的数量,则保留从start开始剩余的所有元素。

返回值:操作成功返回OK。

  • LINSERT key BEFORE|AFTER pivot value

该命令的功能是在pivot元素的前面或后面插入参数中的元素value。如果Key不存在,该命令将不执行任何操作。如果与Key关联的Value类型不是链表,相关的错误信息将被返回。

返回值:返回成功插入后链表中元素的数量,如果没有找到pivot,返回-1,如果key不存在,返回0。

  • RPUSH key value [value …]

在指定Key所关联的List Value的尾部插入参数中给出的所有Values。如果该Key不存在,该命令将在插入之前创建一个与该Key关联的空链表,之后再将数据从链表的尾部插入。如果该键的Value不是链表类型,该命令将返回相关的错误信息。

返回值:插入后链表中元素的数量。

  • RPUSHX key value

仅有当参数中指定的Key存在时,该命令才会在其所关联的List Value的尾部插入参数中给出的Value,否则将不会有任何操作发生。

返回值:插入后链表中元素的数量。

  • RPOP key

返回并弹出指定Key关联的链表中的最后一个元素,即尾部元素,如果该Key不存在,返回nil。

返回值:链表尾部的元素。

  • RPOPLPUSH source destination

原子性的从与source键关联的链表尾部弹出一个元素,同时再将弹出的元素插入到与destination键关联的链表的头部。如果source键不存在,该命令将返回nil,同时不再做任何其它的操作了。如果source和destination是同一个键,则相当于原子性的将其关联链表中的尾部元素移到该链表的头部。

返回值:返回弹出和插入的元素。

3.3、Hash类型及常见操作

Redis中的Hash类型可以看成具有String Key和String Value的map容器。所以该类型非常适合于存储值对象的信息。如用户信息:Username、Password和Age等。每一个Hash可以存储4294967295个键值对。

  • HSET key field value

为指定的Key设定Field/Value对,如果Key不存在,该命令将创建新Key以存储参数中的Field/Value对,如果参数中的Field在该Key中已经存在,则用新值覆盖其原有值。

返回值:1表示新的Field被设置了新值,0表示Field已经存在用新值覆盖原有值。

  • HGET key field

返回指定Key中指定Field的关联值。

返回值:返回参数中Field的关联值,如果参数中的Key或Field不存,返回nil。

  • HSETNX key field value

只有当参数中的Key或Field不存在的情况下,为指定的Key设定Field/Value对,否则该命令不会进行任何操作。

返回值:1表示新的Field被设置了新值,0表示Key或Field已经存在,该命令没有进行任何操作。

  • HEXISTS key field

判断指定Key中的指定Field是否存在。

返回值:1表示存在,0表示不存在。

  • HLEN key

获取该Key所包含的Field的数量。

返回值:返回Key包含的Field数量,如果Key不存在,返回0。

  • HDEL key field [field …]

从指定Key的Hashes Value中删除参数中指定的多个字段,如果不存在的字段将被忽略。

返回值:如果Key不存在,则将其视为空Hashes,并返回0,否则返回实际删除的Field数量。

  • HINCRBY key field increment

增加指定Key中指定Field关联的Value的值。如果Key或Field不存在,该命令将会创建一个新Key或新Field,并将其关联的Value初始化为0,之后再指定数字增加的操作。该命令支持的数字是64位有符号整型,即increment可以负数。

返回值:运算后的值。

  • HGETALL key

获取该键包含的所有Field/Value。其返回格式为一个Field、一个Value,并以此类推。

返回值:Field/Value的列表。

  • HKEYS key

返回指定Key的所有Fields名。

返回值:Field的列表。

  • HVALS key

返回指定Key的所有Values名。

返回值:Value的列表。

  • HMGET key field [field …]

获取和参数中指定Fields关联的一组Values。如果请求的Field不存在,其值返回nil。如果Key不存在,该命令将其视为空Hash,因此返回一组nil。

返回值:返回和请求Fields关联的一组Values,其返回顺序等同于Fields的请求顺序。

  • HMSET key field value [field value …]

逐对依次设置参数中给出的Field/Value对。如果其中某个Field已经存在,则用新值覆盖原有值。如果Key不存在,则创建新Key,同时设定参数中的Field/Value。

返回值:操作成功返回OK。

3.4、Set类型及常见操作

在Redis中,我们可以将Set类型看作为没有排序的字符串集合。Set可包含的最大元素数量是4294967295。Set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络IO开销。

  • SADD key member [member …]

如果在插入的过程用,参数中有的成员在Set中已经存在,该成员将被忽略,而其它成员仍将会被正常插入。如果执行该命令之前,该Key并不存在,该命令将会创建一个新的Set,此后再将参数中的成员陆续插入。

返回值:本次操作实际插入的成员数量。

  • SCARD key

获取Set中成员的数量。

返回值:返回Set中成员的数量,如果该Key并不存在,返回0。

  • SISMEMBER key member

判断参数中指定成员是否已经存在于与Key相关联的Set集合中。

返回值:1表示已经存在,0表示不存在或该Key本身并不存在。

  • SMEMBERS key

获取与该Key关联的Set中所有的成员。

返回值:返回Set中所有的成员。

  • SPOP key

随机的移除并返回Set中的某一成员。由于Set中元素的布局不受外部控制,因此无法像List那样确定哪个元素位于Set的头部或者尾部。

返回值:返回移除的成员,如果该Key并不存在,则返回nil。

  • SRANDMEMBER key

和SPOP一样,随机的返回Set中的一个成员,不同的是该命令并不会删除返回的成员。

返回值:返回随机位置的成员,如果Key不存在则返回nil。

  • SREM key member [member …]

从与Key关联的Set中删除参数中指定的成员,不存在的参数成员将被忽略,如果该Key并不存在,将视为空Set处理。

返回值:从Set中实际移除的成员数量,如果没有则返回0。

  • SMOVE source destination member

原子性的将参数中的成员从source键移入到destination键所关联的Set中。如果该成员在source中并不存在,该命令将不会再执行任何操作并返回0,否则,该成员将从source移入到destination。如果此时该成员已经在destination中存在,那么该命令仅是将该成员从source中移出。

返回值:1表示正常移动,0表示source中并不包含参数成员。

  • SDIFF key [key …]

返回参数中第一个Key所关联的Set和其后所有Keys所关联的Sets中成员的差异。如果Key不存在,则视为空Set。

返回值:差异结果成员的集合。

  • SDIFFSTORE destination key [key …]

该命令和SDIFF命令在功能上完全相同,两者之间唯一的差别是SDIFF返回差异的结果成员,而该命令将差异成员存储在destination关联的Set中。如果destination键已经存在,该操作将覆盖它的成员。

返回值:返回差异成员的数量。

  • SINTER key [key …]

该命令将返回参数中所有Keys关联的Sets中成员的交集。因此如果参数中任何一个Key关联的Set为空,或某一Key不存在,那么该命令的结果将为空集。

返回值:交集结果成员的集合。

  • SINTERSTORE destination key [key …]

该命令和SINTER命令在功能上完全相同,两者之间唯一的差别是SINTER返回交集的结果成员,而该命令将交集成员存储在destination关联的Set中。如果destination键已经存在,该操作将覆盖它的成员。

返回值:返回交集成员的数量。

  • SUNION key [key …]

该命令将返回参数中所有Keys关联的Sets中成员的并集。

返回值:并集结果成员的集合。

  • SUNIONSTORE destination key [key …]

该命令和SUNION命令在功能上完全相同,两者之间唯一的差别是SUNION返回并集的结果成员,而该命令将并集成员存储在destination关联的Set中。如果destination键已经存在,该操作将覆盖它的成员。

返回值:返回并集成员的数量。

3.5、Sorted-Set类型及常见操作

Sorted-Set和Set类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Set中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Set中的成员必须是唯一的,但是分数(score)却是可以重复的。在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,由于Sorted-Sets中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis同样的高效,在其它数据库中进行建模是非常困难的。

  • ZADD key score member [score member …]

添加参数中指定的所有成员及其分数到指定key的Sorted-Sets中,在该命令中我们可以指定多组score/member作为参数。如果在添加时参数中的某一成员已经存在,该命令将更新此成员的分数为新值,同时再将该成员基于新值重新排序。如果键不存在,该命令将为该键创建一个新的Sorted-Sets Value,并将score/member对插入其中。

返回值:本次操作实际插入的成员数量。

  • ZINCRBY key increment member

该命令将为指定Key中的指定成员增加指定的分数。如果成员不存在,该命令将添加该成员并假设其初始分数为0,此后再将其分数加上increment。如果Key不存,该命令将创建该Key及其关联的Sorted-Sets,并包含参数指定的成员,其分数为increment参数。

返回值:以字符串形式表示的新分数。

  • ZCARD key

获取与该Key相关联的Sorted-Sets中包含的成员总数量。

返回值:返回Sorted-Sets中的成员数量,如果该Key不存在,返回0。

  • ZCOUNT key min max

该命令用于获取分数(score)在min和max之间的成员数量。缺省情况下,min和max表示的范围是闭区间范围,即min <= score <= max内的成员将被返回。然而我们可以通过在min和max的前面添加"("字符来表示开区间,如(min max表示min < score <= max,而(min (max表示min < score < max。

返回值:分数指定范围内成员的数量。

  • ZRANGE key start stop [WITHSCORES]

该命令返回排名在参数start和stop指定范围内的成员,这里start和stop参数都是以0开始的,即0表示第一个成员,-1表示最后一个成员。如果start大于该Sorted-Sets中的最大索引值或start > stop,此时一个空集合将被返回。如果stop大于最大索引值,该命令将返回从start到集合的最后一个成员。如果命令中带有可选参数WITHSCORES选项,该命令在返回的结果中将包含每个成员的分数值,如value1,score1,value2,score2…。

返回值:返回索引在start和stop之间的成员列表。

  • ZREVRANGE key start stop [WITHSCORES]

该命令的功能和ZRANGE基本相同,唯一的差别在于顺序相反。

返回值:返回指定的成员列表。

  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

该命令将返回分数在min和max范围内的成员,即满足表达式min <= score <= max的成员,其中返回的成员是按照其分数从低到高的顺序返回,如果成员具有相同的分数,则按成员的字典顺序返回。可选参数LIMIT用于限制返回成员的数量范围。可选参数offset表示从符合条件的第offset个成员开始返回,同时返回count个成员。

返回值:返回分数在指定范围内的成员列表。

  • ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

该命令除了排序方式是基于从高到低的分数排序之外,其它功能和参数含义均与ZRANGEBYSCORE相同。

返回值:返回分数在指定范围内的成员列表。

  • ZRANK key member

该命令将返回参数中指定成员的位置值(按分数由低到高的顺序),其中0表示第一个成员,它是Sorted-Sets中分数最低的成员。

返回值:如果该成员存在,则返回它的位置索引值,否则返回nil。

  • ZREVRANK key member

该命令的功能和ZRANK基本相同,唯一的差别在于顺序相反。

返回值:如果该成员存在,则返回它的位置索引值,否则返回nil。

  • ZSCORE key member

获取指定成员的分数。
返回值:如果该成员存在,以字符串的形式返回其分数,否则返回nil。

  • ZREM key member [member …]

该命令将移除参数中指定的成员,其中不存在的成员将被忽略。如果与该Key关联的Value不是Sorted-Sets,相应的错误信息将被返回。

返回值:实际被删除的成员数量。

  • ZREMRANGEBYRANK key start stop

删除索引位置位于start和stop之间的成员,start和stop都是以0开始的,即0表示分数最低的成员,-1表示最后一个成员,即分数最高的成员。

返回值:被删除的成员数量。

  • ZREMRANGEBYSCORE key min max

删除分数在min和max之间的所有成员,即满足表达式min <= score <= max的所有成员。对于min和max参数,可以采用开区间的方式表示,具体规则参照ZCOUNT。

返回值:被删除的成员数量。

第四章 Redis通用操作

  • KEYS pattern

获取所有匹配pattern参数的Keys。需要说明的是,在我们的正常操作中应该尽量避免对该命令的调用,因为对于大型数据库而言,该命令是非常耗时的,对Redis服务器的性能打击也是比较大的。pattern支持glob-style的通配符格式,如*表示任意一个或多个字符,?表示任意字符,[abc]表示方括号中任意一个字母。匹配模式的键列表。

返回值:返回所有匹配的key。

  • DEL key [key …]

从数据库删除中参数中指定的keys,如果指定键不存在,则直接忽略。还需要另行指出的是,如果指定的Key关联的数据类型不是String类型,而是List、Set、Hashes和Sorted Set等容器类型,该命令删除每个键的时间复杂度为O(M),其中M表示容器中元素的数量。而对于String类型的Key,其时间复杂度为O(1)。

返回值:实际被删除的Key数量。

  • EXISTS key

判断指定键是否存在。

返回值:1表示存在,0表示不存在。

  • MOVE key db

将当前数据库中指定的键Key移动到参数中指定的数据库中。如果该Key在目标数据库中已经存在,或者在当前数据库中并不存在,该命令将不做任何操作。在redis.conf文件中定义了redis的默认库的数据,默认一个redis有16个库,下标为0-15。我们可以使用select 数值来进行库的切换:select 10。

返回值:移动成功返回1,否则不做任何操作返回0。

  • RENAME key newkey

为指定的键重新命名,如果参数中的两个Keys的名字相同,或者是源Key不存在,该命令都会返回相关的错误信息。如果newKey已经存在,则直接覆盖。

返回值:操作成功返回OK。

  • RENAMENX key newkey

如果新值不存在,则将参数中的原值修改为新值,其它条件和RENAME一致。

返回值:1表示修改成功,否则0。

  • PERSIST key

如果Key存在过期时间,该命令会将其过期时间消除,使该Key不再有超时,而是可以持久化存储。

返回值:1表示Key的过期时间被移除,0表示该Key不存在或没有过期时间。

  • EXPIRE key seconds

该命令为参数中指定的Key设定超时的秒数,在超过该时间后,Key被自动的删除。如果该Key在超时之前被修改,与该键关联的超时将被移除。

返回值:1表示超时被设置,0则表示Key不存在或不能被设置。

  • EXPIREAT key timestamp

该命令的逻辑功能和EXPIRE完全相同,唯一的差别是该命令指定的超时时间是绝对时间,而不是相对时间。该时间参数是Unix timestamp格式的,即从1970年1月1日开始所流经的秒数。

返回值:1表示超时被设置,0则表示Key不存在或不能被设置。

  • TTL key

获取该键所剩的超时描述。

返回值:返回所剩描述,如果该键不存在或没有超时设置,则返回-1。

  • RANDOMKEY

从当前打开的数据库中随机的返回一个Key。

返回值:返回的随机键,如果该数据库是空的则返回nil。

  • TYPE key

获取与参数中指定键关联值的类型,该命令将以字符串的格式返回。

返回值:返回的字符串为string、list、set、hash和zset(Sorted-Set),如果key不存在返回none。

第五章 Redis事务管理

5.1、Redis事务的两个保证

Redis事务可以一次执行多个命令并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

5.2、Redis事务的三个阶段

Redis一个事务从开始到执行会经历以下三个阶段:

  • 开始事务:MULTI
  • 命令入队
  • 执行事务:EXEC

5.3、Redis事务的常见命令

  • MULTI

Multi命令用于标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由EXEC命令原子性(atomic)地执行。

返回值:总是返回OK。

  • EXEC

Exec命令用于执行所有事务块内的所有命令。

返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列,当操作被打断时,返回空值nil 。

  • DISCARD

Discard命令用于放弃执行事务块内的所有命令。

返回值:总是返回OK 。

  • WATCH

Watch命令用于监视一个(或多个) key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

  • UNWATCH

Unwatch命令用于取消 WATCH 命令对所有key的监视。

第六章 Redis数据持久化

Redis将内存存储和持久化存储相结合,即可提供数据访问的高效性,又可保证数据存储的安全性。

6.1、Redis数据持久化机制

  1. RDB持久化:该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
  2. AOF持久化:该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。
  3. 同时应用RDB和AOF:同时配置这两种方式即可。
  4. 无持久化:可通过配置的方式禁用Redis服务器的持久化功能,这样我们就可以将Redis视为一个功能加强版的memcached了。

6.2、Redis数据持久化配置

  1. RDB快照存储方式:缺省情况下,Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开redis.conf文件之后,我们搜索save,可以看到下面的配置信息:

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

    注意:关于dump.rdb文件存储的位置,它是设置是在redis.conf文件中,dir ./,代表了服务器启动时的当前路径。

  2. AOF日志文件方式:AOF是append only file的缩写,它默认是关闭的,需要在redis.conf中打开,将appendonly no改为appendonly yes,在Redis的配置文件中存在三种同步方式,它们分别是:

    • appendfsync always:#每次有数据修改发生时都会写入AOF文件。
    • appendfsync everysec:#每秒钟同步一次,该策略为AOF的缺省策略。
    • appendfsync no:#从不同步,高效但是数据不会被持久化。

6.3、RDB与AOF对比总结

  • RDB存在哪些优势呢?
    • 数据的备份和恢复非常方便,因为一个数据库只有一个持久化文件。
    • 性能最大化,对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
    • 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
  • RDB又存在哪些劣势呢?
    • 系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。
    • 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
  • AOF的优势有哪些呢?
    • 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3种同步策略,即每秒同步、每修改同步和不同步。
    • 对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。
    • 如果日志过大,Redis可以自动启用rewrite机制迅速“瘦身”(也可手动触发AOF的rewrite操作,命令: bgrewriteaof)。
    • AOF日志格式清晰、易于理解,很容易用AOF日志文件完成数据的重建。
  • AOF的劣势有哪些呢?
    • 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。
    • 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

第七章 Redis主从复制

7.1、主从复制介绍

Redis要达到高并发、高可用,一个Redis是不可行的,必须给Redis建立集群,以一个master和多个slave的模式工作,主要是master写,多个salve读,即是读写分离的情况,以提高服务器的负载能力。主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

7.2、主从复制特点

主节点master可读可写,从节点slave只能读,主节点将读的操作交给了从节点,变向的提高了自己写的能力。

7.3、架构具体实现

第一步:环境概述

注意:建议在Linux系统进行操作本章内容之前,用虚拟机进行一个快照操作,每一次操作我都会使用快照功能还原到刚安装的初始状态。

虚拟机版本:VMware-workstation-full-15.5.6-16341506.exe
Linux版本:CentOS-6.10-x86_64-bin-DVD1.iso,桌面版,全新安装,可上网
Redis版本:redis-5.0.9.tar.gz
连接的工具:SecureCRT7.0.0,会话字符集utf-8

第二步:软件下载

注意:默认我已经认为你了解Redis的基本操作和安装,如果不懂请看前边的Redis文章学习!所有集群操作都是在一台Linux上完成,所以IP都一样,在这里我们使用端口号来模拟不同的服务器,而在真实的集群中也只不过是IP地址不同端口相同而已,所以,这并不会影响我们去实现搭建效果,我们将redis安装到/usr/local/redis这个路径,以后启动的时候,使用配置文件启动,一个配置文件就相当于一个Redis实例,由于是单机版集群,所以不需要安装许多份,但是在真正的集群下还是需要安装的,每一个服务器都需要安装一个。

[root@caochenlei ~]# yum -y install gcc gcc-c++
[root@caochenlei ~]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz
[root@caochenlei ~]# tar -zxvf redis-5.0.9.tar.gz
[root@caochenlei ~]# cd redis-5.0.9
[root@caochenlei redis-5.0.9]# make
[root@caochenlei redis-5.0.9]# make PREFIX=/usr/local/redis install

#查询IP地址,由于我们是单机版集群,所有Redis实例的服务器地址都一样也就是当前Linux系统的主机地址,但是它们的端口不一样
[root@caochenlei redis-5.0.9]# ifconfig

第三步:架构描述

架构:我们要实现的是一主两从,所以需要三台Redis服务器,在这里我们以8001、8002、8003三个端口为例进行操作,具体IP请看ifconfig查询的地址。

image-20200817011443675

第四步:创建目录

每一个目录就对应上图的一个Redis实例

[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis8001
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis8002
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis8003

第五步:修改配置

[root@caochenlei redis-5.0.9]# vim redis.conf

注意:修改一下几项内容,然后保存退出

#1、注释本地回环地址,开放所有IP
#bind 127.0.0.1

#2、将保护模式修改为no
protected-mode no

#3、是否使用后台进程修改为yes
daemonize yes

#4、修改日志输出地方,这里使用6379,方便我们后来将所有的6379替换为8001、8002、8003
logfile "/redis-cluster/redis6379/logfile.log"

#5、修改输出文件路径dir,这里使用6379,方便我们后来将所有的6379替换为8001、8002、8003
dir /redis-cluster/redis6379/

#6、添加主机访问密码,在# masterauth <master-password>注释下面添加
masterauth 123456

#7、开启密码访问
requirepass 123456

第六步:复制配置

将咱们刚才修改的文件中的6379端口依次替换为8001、8002、8003并重新写入到相对应的文件中

[root@caochenlei redis-5.0.9]# sed 's/6379/8001/g' redis.conf > /redis-cluster/redis8001/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/8002/g' redis.conf > /redis-cluster/redis8002/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/8003/g' redis.conf > /redis-cluster/redis8003/redis.conf

第七步:从机配置

[root@caochenlei redis-5.0.9]# vim /redis-cluster/redis8002/redis.conf

注意:添加从机所对应的主机(master)的ip地址和端口号,然后保存退出

#添加主机ip和端口,在# replicaof <masterip> <masterport>注释下面添加
replicaof 192.168.190.128 8001

[root@caochenlei redis-5.0.9]# vim /redis-cluster/redis8003/redis.conf

注意:添加从机所对应的主机(master)的ip地址和端口号,然后保存退出

#添加主机ip和端口,在# replicaof <masterip> <masterport>注释下面添加
replicaof 192.168.190.128 8001

第八步:启动测试

启动服务器:一主两从

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis8001/redis.conf 
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis8002/redis.conf 
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis8003/redis.conf 

启动客户端:重新打开三个会话,依次启动

image-20200817010154911

#192.168.190.128(1)进入后的命令:启动master(主机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 8001 -a 123456

#192.168.190.128(2)进入后的命令:启动slave(从机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 8002 -a 123456

#192.168.190.128(3)进入后的命令:启动slave(从机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 8003 -a 123456

开始测试:

打开主机【#192.168.190.128(1)】界面,使用set user zhangsan

打开从机【#192.168.190.128(2)】界面,使用get user,可以查看查询到的结果,但是不能使用set

打开从机【#192.168.190.128(3)】界面,使用get user,可以查看查询到的结果,但是不能使用set

打开主机【启动服务器:一主两从】那个界面,使用ps -ef | grep redis,找到Redis8001实例所对应的pid,然后kill掉,格式kill 对应的pid,以此来模拟主机宕机

打开主机【#192.168.190.128(1)】界面,使用get user,你会发现Could not connect to Redis at 192.168.190.128:8001: Connection refused,主机宕机

打开从机【#192.168.190.128(2)】界面,使用get user,查看查询到的结果,结果依然可以查询,但是不能使用set

打开从机【#192.168.190.128(3)】界面,使用get user,查看查询到的结果,结果依然可以查询,但是不能使用set

注意:当主机宕机之后,将不能进行写操作,需要手动将从机升级为主机,从机需要重新制定master,主从复制只能保证主机宕机后,依然可以获取数据

第八章 Redis哨兵模式

8.1、哨兵模式介绍

在Redis 2.8版本开始引入,哨兵的核心功能是主节点的自动故障转移。通俗来讲哨兵模式的出现是就是为了解决我们主从复制模式中需要我们人为操作的东西变为自动版并且它比人为要更及时。

8.2、哨兵主要特点

  • 监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。

  • 自动故障转移(Automatic Failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其它从节点改为复制新的主节点。

  • 配置提供者(Configuration Provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。

  • 通知(Notification):哨兵可以将故障转移的结果发送给客户端。

    其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移,而配置提供者和通知功能,则需要在与客户端的交互中才能体现。

8.3、架构具体实现

第一步:环境概述

注意:建议在Linux系统进行操作本章内容之前,用虚拟机进行一个快照操作,每一次操作我都会使用快照功能还原到刚安装的初始状态。

虚拟机版本:VMware-workstation-full-15.5.6-16341506.exe
Linux版本:CentOS-6.10-x86_64-bin-DVD1.iso,桌面版,全新安装,可上网
Redis版本:redis-5.0.9.tar.gz
连接的工具:SecureCRT7.0.0,会话字符集utf-8

第二步:软件下载

注意:默认我已经认为你了解Redis的基本操作和安装,如果不懂请看前边的Redis文章学习!所有集群操作都是在一台Linux上完成,所以IP都一样,在这里我们使用端口号来模拟不同的服务器,而在真实的集群中也只不过是IP地址不同端口相同而已,所以,这并不会影响我们去实现搭建效果,我们将redis安装到/usr/local/redis这个路径,以后启动的时候,使用配置文件启动,一个配置文件就相当于一个Redis实例,由于是单机版集群,所以不需要安装许多份,但是在真正的集群下还是需要安装的,每一个服务器都需要安装一个。

[root@caochenlei ~]# yum -y install gcc gcc-c++
[root@caochenlei ~]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz
[root@caochenlei ~]# tar -zxvf redis-5.0.9.tar.gz
[root@caochenlei ~]# cd redis-5.0.9
[root@caochenlei redis-5.0.9]# make
[root@caochenlei redis-5.0.9]# make PREFIX=/usr/local/redis install

#查询IP地址,由于我们是单机版集群,所有Redis实例的服务器地址都一样也就是当前Linux系统的主机地址,但是它们的端口不一样
[root@caochenlei redis-5.0.9]# ifconfig

第三步:架构描述

架构描述:我们要实现的是一主两从,所以需要三台Redis服务器,端口分别为7001、7002、7003,同时我们也需要三台哨兵,让哨兵盯着点一主两从,只要这个一主两从的主节点发生了故障,它就会自动的帮我们进行自动故障转移,然后重新选举其中的一个slave节点作为主节点重新上线。通俗的来讲,就是一个包工头盯着手底下人干活,一旦手底下的一个小组长生病了来不了,那好办,这个包工头群里就开会商量重新找一个小组长来顶替之前的那个人的任务。但实际上,我们所说的这个哨兵,本质上是特殊的Redis节点,3个哨兵节点的配置几乎是完全一样的,主要区别在于端口号的不同(26379 / 26380 / 26381)。

image-20200817123433971

第四步:创建目录

每一个目录就对应上图的一个Redis实例

[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis7001
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis7002
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis7003

[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/sentinel26379
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/sentinel26380
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/sentinel26381

第五步:修改配置

修改的是redis.conf配置文件:

[root@caochenlei redis-5.0.9]# vim redis.conf

注意:修改一下几项内容,然后保存退出

#1、注释本地回环地址,开放所有IP
#bind 127.0.0.1

#2、将保护模式修改为no
protected-mode no

#3、是否使用后台进程修改为yes
daemonize yes

#4、修改日志输出地方,这里使用6379,方便我们后来将所有的6379替换为7001、7002、7003
logfile "/redis-cluster/redis6379/logfile.log"

#5、修改输出文件路径dir,这里使用6379,方便我们后来将所有的6379替换为7001、7002、7003
dir /redis-cluster/redis6379/

#6、添加主机访问密码,在# masterauth <master-password>注释下面添加
masterauth 123456

#7、开启密码访问
requirepass 123456

修改的是sentinel.conf配置文件

[root@caochenlei redis-5.0.9]# vim sentinel.conf

注意:修改一下几项内容,然后保存退出

#1、修改一下端口为6379,在这里我们先写死,方便我们后来将所有的6379替换为26379、26380、26381
port 6379

#2、是否使用后台进程修改为yes
daemonize yes

#3、修改pid路径
pidfile /var/run/redis-sentinel-6379.pid

#4、修改哨兵的输出日志路径,在这里我们先写死,方便我们后来将所有的6379替换为26379、26380、26381
logfile "/redis-cluster/sentinel6379/logfile.log"

#5、修改文件默认输出路径
dir /redis-cluster/sentinel6379/

#6、修改一条主机的记录,告诉哨兵,我的主机在哪,原来是这个sentinel monitor mymaster 127.0.0.1 6379 2,修改成下边这个,配置的含义是:该哨兵节点监控192.168.190.128 7001这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。一般哨兵个数为奇数,最少为3个,而最后这个数字的计算是哨兵个数/2+1,也就是说一半以上的人说它坏了它就坏了,接下来一行,告诉哨兵监控的这个主机的密码是多少,防止在监控的时候连接失败
sentinel monitor mymaster 192.168.190.128 7001 2
sentinel auth-pass mymaster 123456

第六步:复制配置

将咱们刚才修改的文件中的6379端口依次替换为7001、7002、7003并重新写入到相对应的文件中

[root@caochenlei redis-5.0.9]# sed 's/6379/7001/g' redis.conf > /redis-cluster/redis7001/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/7002/g' redis.conf > /redis-cluster/redis7002/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/7003/g' redis.conf > /redis-cluster/redis7003/redis.conf

[root@caochenlei redis-5.0.9]# sed 's/6379/26379/g' sentinel.conf > /redis-cluster/sentinel26379/sentinel.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/26380/g' sentinel.conf > /redis-cluster/sentinel26380/sentinel.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/26381/g' sentinel.conf > /redis-cluster/sentinel26381/sentinel.conf

第七步:从机配置

[root@caochenlei redis-5.0.9]# vim /redis-cluster/redis7002/redis.conf

注意:添加从机所对应的主机(master)的ip地址和端口号,然后保存退出

#添加主机ip和端口,在# replicaof <masterip> <masterport>注释下面添加
replicaof 192.168.190.128 7001

[root@caochenlei redis-5.0.9]# vim /redis-cluster/redis7003/redis.conf

注意:添加从机所对应的主机(master)的ip地址和端口号,然后保存退出

#添加主机ip和端口,在# replicaof <masterip> <masterport>注释下面添加
replicaof 192.168.190.128 7001

第八步:启动测试

启动服务器:一主两从

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis7001/redis.conf 
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis7002/redis.conf 
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis7003/redis.conf 

启动哨兵端:三个哨兵

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-sentinel /redis-cluster/sentinel26379/sentinel.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-sentinel /redis-cluster/sentinel26380/sentinel.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-sentinel /redis-cluster/sentinel26381/sentinel.conf

启动客户端:重新打开三个会话,依次启动

image-20200817010154911

#192.168.190.128(1)进入后的命令:启动master(主机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 7001 -a 123456

#192.168.190.128(2)进入后的命令:启动slave(从机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 7002 -a 123456

#192.168.190.128(3)进入后的命令:启动slave(从机)
[root@caochenlei ~]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 7003 -a 123456

开始测试:

打开主机【#192.168.190.128(1)】界面,使用set user zhangsan

打开从机【#192.168.190.128(2)】界面,使用get user,可以查看查询到的结果,但是不能使用set

打开从机【#192.168.190.128(3)】界面,使用get user,可以查看查询到的结果,但是不能使用set

打开主机【启动服务器:一主两从】那个界面,使用ps -ef | grep redis,找到Redis7001实例所对应的pid,然后kill掉,格式kill 对应的pid,以此来模拟主机宕机

打开主机【#192.168.190.128(1)】界面,使用get user,你会发现Could not connect to Redis at 192.168.190.128:7001: Connection refused,主机宕机

打开从机【#192.168.190.128(2)】界面,使用get user查询结果,结果可以查询,也可以使用set,我们在使用info replication,我们会发现原来的这个slave已经变成了master(role:master)

192.168.190.128:7002> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.190.128,port=7003,state=online,offset=364154,lag=1
master_replid:b0d933061991f1dd4057cf0f54a8cf02e457ade7
master_replid2:0de54711430f88e039ebb073c1668187e297d318
master_repl_offset:364299
second_repl_offset:6759
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:364299
192.168.190.128:7002> 

打开从机【#192.168.190.128(3)】界面,使用get user,查看查询到的结果,结果依然可以查询,但是不能使用set,我们在使用info replication,我们会发现原来的这个slave还是slave(role:slave),它对应的master变成了7002

192.168.190.128:7003> info replication
# Replication
role:slave
master_host:192.168.190.128
master_port:7002
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:379912
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b0d933061991f1dd4057cf0f54a8cf02e457ade7
master_replid2:0de54711430f88e039ebb073c1668187e297d318
master_repl_offset:379912
second_repl_offset:6759
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:459
repl_backlog_histlen:379454
192.168.190.128:7003> 

这就说明了,在7001这个主机发生故障的时候,哨兵自动的选举了一个新的master(主机)它就是7002,然后让其它的所有salve重新连接到7002这个主机上,已经故障的7001已经被哨兵踢出去了,如果当7001节点再次上线时,该节点成为新主节点7002的从节点,这也是由哨兵自动完成的。

第九章 Redis高可用集群

9.1、高可用集群介绍

Redis Cluster集群是一个由多个主从节点集群组成的分布式服务器群,它具有复制、高可用和分片特性。

9.2、高可用集群特点

Redis Cluster集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到1000节点。Redis Cluster集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

9.3、架构具体的实现

第一步:环境概述

注意:建议在Linux系统进行操作本章内容之前,用虚拟机进行一个快照操作,每一次操作我都会使用快照功能还原到刚安装的初始状态。

虚拟机版本:VMware-workstation-full-15.5.6-16341506.exe
Linux版本:CentOS-6.10-x86_64-bin-DVD1.iso,桌面版,全新安装,可上网
Redis版本:redis-5.0.9.tar.gz
连接的工具:SecureCRT7.0.0,会话字符集utf-8

第二步:软件下载

注意:默认我已经认为你了解Redis的基本操作和安装,如果不懂请看前边的Redis文章学习!所有集群操作都是在一台Linux上完成,所以IP都一样,在这里我们使用端口号来模拟不同的服务器,而在真实的集群中也只不过是IP地址不同端口相同而已,所以,这并不会影响我们去实现搭建效果,我们将redis安装到/usr/local/redis这个路径,以后启动的时候,使用配置文件启动,一个配置文件就相当于一个Redis实例,由于是单机版集群,所以不需要安装许多份,但是在真正的集群下还是需要安装的,每一个服务器都需要安装一个。

[root@caochenlei ~]# yum -y install gcc gcc-c++
[root@caochenlei ~]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz
[root@caochenlei ~]# tar -zxvf redis-5.0.9.tar.gz
[root@caochenlei ~]# cd redis-5.0.9
[root@caochenlei redis-5.0.9]# make
[root@caochenlei redis-5.0.9]# make PREFIX=/usr/local/redis install

#查询IP地址,由于我们是单机版集群,所有Redis实例的服务器地址都一样也就是当前Linux系统的主机地址,但是它们的端口不一样
[root@caochenlei redis-5.0.9]# ifconfig

第三步:架构描述

架构描述:我们要实现的是子集群是一主两从的高可用集群,自己群一共是3组,一共是9台机器,主机跟从机的比例是1:2,也就是3台主机对应6台从机。

image-20200817230610925

第四步:创建目录

每一个目录就对应上图的一个Redis实例

[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6001
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6002
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6003
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6004
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6005
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6006
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6007
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6008
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6009

第五步:修改配置

修改的是redis.conf配置文件:

[root@caochenlei redis-5.0.9]# vim redis.conf

注意:修改一下几项内容,然后保存退出

#1、注释本地回环地址,开放所有IP
#bind 127.0.0.1

#2、将保护模式修改为no
protected-mode no

#3、是否使用后台进程修改为yes
daemonize yes

#4、修改日志输出地方,这里使用6379,方便我们后来将所有的6379替换为6001、6002、...、6009
logfile "/redis-cluster/redis6379/logfile.log"

#5、修改输出文件路径dir,这里使用6379,方便我们后来将所有的6379替换为6001、6002、...、6009
dir /redis-cluster/redis6379/

#6、添加主机访问密码,在# masterauth <master-password>注释下面添加
masterauth 123456

#7、开启密码访问
requirepass 123456

#8、找到################################ REDIS CLUSTER  ###############################,该模块下所有配置的注释取消
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
cluster-migration-barrier 1
cluster-require-full-coverage yes
cluster-replica-no-failover no
################################# REDIS CLUSTER  ###############################,具体含义如下:
#cluster-enabled yes                  开启集群
#cluster-config-file nodes-6379.conf  该节点的配置文件(集群自动生成的文件)
#cluster-node-timeout 15000           节点超时时间,超过这个时间该节点不可用,毫秒
#cluster-replica-validity-factor 10   较大的副本有效性因子可能会使数据太旧的副本无法转移到主服务器,而太小的值可能会使集群根本无法选择副本。
#cluster-migration-barrier 1          迁移障碍为1意味着一个副本只有在其主副本至少有1个工作副本时才会迁移(默认值为1)
#cluster-require-full-coverage yes    主节点宕机,此时没有从节点进行恢复,no表示,当一个插槽节点不工作,则该槽位停止接收查询操作,集群仍然正常工作,如果为yes,则任意一个槽位不可用则整个集群不可用
#cluster-replica-no-failover no       no表示,禁止主服务器转移

第六步:复制配置

将咱们刚才修改的文件中的6379端口依次替换为6001、6002、…、6009并重新写入到相对应的文件中

[root@caochenlei redis-5.0.9]# sed 's/6379/6001/g' redis.conf > /redis-cluster/redis6001/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6002/g' redis.conf > /redis-cluster/redis6002/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6003/g' redis.conf > /redis-cluster/redis6003/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6004/g' redis.conf > /redis-cluster/redis6004/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6005/g' redis.conf > /redis-cluster/redis6005/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6006/g' redis.conf > /redis-cluster/redis6006/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6007/g' redis.conf > /redis-cluster/redis6007/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6008/g' redis.conf > /redis-cluster/redis6008/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6009/g' redis.conf > /redis-cluster/redis6009/redis.conf

第七步:依次启动

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6001/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6002/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6003/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6004/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6005/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6006/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6007/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6008/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6009/redis.conf

第八步:主从关联

执行命令:

/usr/local/redis/bin/redis-cli -a "123456" --cluster create \
192.168.190.128:6001 \
192.168.190.128:6002 \
192.168.190.128:6003 \
192.168.190.128:6004 \
192.168.190.128:6005 \
192.168.190.128:6006 \
192.168.190.128:6007 \
192.168.190.128:6008 \
192.168.190.128:6009 \
--cluster-replicas 2

执行效果:

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -a "123456" --cluster create \
> 192.168.190.128:6001 \
> 192.168.190.128:6002 \
> 192.168.190.128:6003 \
> 192.168.190.128:6004 \
> 192.168.190.128:6005 \
> 192.168.190.128:6006 \
> 192.168.190.128:6007 \
> 192.168.190.128:6008 \
> 192.168.190.128:6009 \
> --cluster-replicas 2
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 9 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.190.128:6005 to 192.168.190.128:6001
Adding replica 192.168.190.128:6006 to 192.168.190.128:6001
Adding replica 192.168.190.128:6007 to 192.168.190.128:6002
Adding replica 192.168.190.128:6008 to 192.168.190.128:6002
Adding replica 192.168.190.128:6009 to 192.168.190.128:6003
Adding replica 192.168.190.128:6004 to 192.168.190.128:6003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 7a3d69651451df47fff7628d545e78e9605e87fd 192.168.190.128:6001
   slots:[0-5460] (5461 slots) master
M: ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273 192.168.190.128:6002
   slots:[5461-10922] (5462 slots) master
M: b348d45ee154640bfa1311b8aa9044c15ee7e433 192.168.190.128:6003
   slots:[10923-16383] (5461 slots) master
S: 100368e3a309dc5e5ae1b37173cb4c523bb788d8 192.168.190.128:6004
   replicates b348d45ee154640bfa1311b8aa9044c15ee7e433
S: a9a80d7117ce51546a532dcf71c67f7419756f65 192.168.190.128:6005
   replicates ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273
S: b780ab64c35876a01bd3a7b9ffa29f7e6db0a05f 192.168.190.128:6006
   replicates 7a3d69651451df47fff7628d545e78e9605e87fd
S: b0474981e505cfae6bcc4c451637d153ac9f9d80 192.168.190.128:6007
   replicates b348d45ee154640bfa1311b8aa9044c15ee7e433
S: ab63c9d31cc4c2b016a45c0adcf3a0512900251d 192.168.190.128:6008
   replicates 7a3d69651451df47fff7628d545e78e9605e87fd
S: 742a3324b1604976f1941764b4dd32769e05f205 192.168.190.128:6009
   replicates ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273
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 192.168.190.128:6001)
M: 7a3d69651451df47fff7628d545e78e9605e87fd 192.168.190.128:6001
   slots:[0-5460] (5461 slots) master
   2 additional replica(s)
S: b0474981e505cfae6bcc4c451637d153ac9f9d80 192.168.190.128:6007
   slots: (0 slots) slave
   replicates b348d45ee154640bfa1311b8aa9044c15ee7e433
S: 100368e3a309dc5e5ae1b37173cb4c523bb788d8 192.168.190.128:6004
   slots: (0 slots) slave
   replicates b348d45ee154640bfa1311b8aa9044c15ee7e433
S: b780ab64c35876a01bd3a7b9ffa29f7e6db0a05f 192.168.190.128:6006
   slots: (0 slots) slave
   replicates 7a3d69651451df47fff7628d545e78e9605e87fd
S: a9a80d7117ce51546a532dcf71c67f7419756f65 192.168.190.128:6005
   slots: (0 slots) slave
   replicates ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273
S: 742a3324b1604976f1941764b4dd32769e05f205 192.168.190.128:6009
   slots: (0 slots) slave
   replicates ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273
S: ab63c9d31cc4c2b016a45c0adcf3a0512900251d 192.168.190.128:6008
   slots: (0 slots) slave
   replicates 7a3d69651451df47fff7628d545e78e9605e87fd
M: b348d45ee154640bfa1311b8aa9044c15ee7e433 192.168.190.128:6003
   slots:[10923-16383] (5461 slots) master
   2 additional replica(s)
M: ae3cc616e84ed6e2ae053a78e68a1ec8ed15a273 192.168.190.128:6002
   slots:[5461-10922] (5462 slots) master
   2 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@caochenlei redis-5.0.9]#

第九步:测试集群

查看关系

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 6001 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.190.128:6001> cluster nodes

image-20200817181042264

添加数据

你会发现当你添加一个username的时候,它报错了,你该想了,这不集群搭建完成了吗,怎么保存不了?

192.168.190.128:6001> set username zhangsan
(error) MOVED 14315 192.168.190.128:6003

你再试试set hello world,你会发现又成功了,这是怎么回事呢?其实上边的那条数据它的key=username,我们先要拿它进行crc16运算等等得到一个槽位,结果槽位是在192.168.190.128:6003这个master上,而你当前登录的是192.168.190.128:6001,那肯定保存不了,而当key=hello的时候,计算得到的槽位=886,显然是在当前的master中的,所以可以存储,那该怎么办呢?

192.168.190.128:6001> set hello world
OK

我们可以在连接参数的最后边添加一个-c,它就代表了可以自动重定向到相对应的槽位的master,记住,这个-c只有redis-cli所独有,jedis中是没有的

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 6001 -a 123456 -c
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.190.128:6001> set username zhansgan
-> Redirected to slot [14315] located at 192.168.190.128:6003
OK
192.168.190.128:6003> 

9.4、高可用集群扩容

既然我们要扩容,要扩容就需要扩一组,那也就是还需要三台Redis实例(一主两从),按照以上的步骤继续来创建,命令如下:

[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6010
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6011
[root@caochenlei redis-5.0.9]# mkdir -p /redis-cluster/redis6012

然后将配置文件拷贝到相对应的文件夹,每一个配置文件就相当一台Redis实例,这样我们就有3台Redis了

[root@caochenlei redis-5.0.9]# sed 's/6379/6010/g' redis.conf > /redis-cluster/redis6010/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6011/g' redis.conf > /redis-cluster/redis6011/redis.conf
[root@caochenlei redis-5.0.9]# sed 's/6379/6012/g' redis.conf > /redis-cluster/redis6012/redis.conf

然后依次启动这三个Redis

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6010/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6011/redis.conf
[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-server /redis-cluster/redis6012/redis.conf

我们要开始使用redis-cli扩容了,其实就是一句话的问题,至于这个–cluster-master-id你其实可以不用指定,它会自动选择6010作为master,6011、6012作为6010的salve从节点

/usr/local/redis/bin/redis-cli -a "123456" --cluster add-node 192.168.190.128:6010 192.168.190.128:6001
/usr/local/redis/bin/redis-cli -a "123456" --cluster add-node 192.168.190.128:6011 192.168.190.128:6001 --cluster-slave
/usr/local/redis/bin/redis-cli -a "123456" --cluster add-node 192.168.190.128:6012 192.168.190.128:6001 --cluster-slave

然后我们登录到6010,查看一下现在集群的情况,myself, master代表当前登录的是哪个master,可以看到,6011、6012已经成为6010的子节点了

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 6010 -a 123456 -c

image-20200817183708871

虽然这三台Redis已经进入到集群了,但是6010这个主mater他还没有槽位,没有槽位就意味着不能操作,接下来分配槽位

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -a "123456" --cluster reshard 192.168.190.128:6010

image-20200817193617915

到这里输入 yes 回车即可

Do you want to proceed with the proposed reshard plan (yes/no)? yes

重新连接6010查看节点

image-20200817193901349

9.5、高可用集群缩容

先将槽位分出去,然后在删除salve子节点,然后再删除master主节点,我们将6010的1500个槽位全部分给6001这个主节点,当然了,你也可以给了别的分

192.168.190.128:6001:这个只要是集群中的Redis都可以写,这个IP和端口的意思就是连接到集群

–cluster-from 9c18573aab51886394a9db0bbee592db281605b5:将6010的所有槽位

–cluster-to 7a3d69651451df47fff7628d545e78e9605e87fd:移动到6001中

–cluster-slots 1500:移动多少个呢?移动1500个

/usr/local/redis/bin/redis-cli -a "123456" --cluster reshard 192.168.190.128:6001 \
--cluster-from 9c18573aab51886394a9db0bbee592db281605b5 \
--cluster-to 7a3d69651451df47fff7628d545e78e9605e87fd \
--cluster-slots 1500

然后依次删除salve和master,192.168.190.128:6001:这个只要是集群中的Redis都可以写,这个IP和端口的意思就是连接到集群

/usr/local/redis/bin/redis-cli -a "123456" --cluster del-node 192.168.190.128:6001 2df58193e826c69f53db9c4bd9df07f228a2f5b5
/usr/local/redis/bin/redis-cli -a "123456" --cluster del-node 192.168.190.128:6001 704c2968a006ba52a850c2f6f4e04a551f3f670d
/usr/local/redis/bin/redis-cli -a "123456" --cluster del-node 192.168.190.128:6001 9c18573aab51886394a9db0bbee592db281605b5

然后我们登录到6001中,查看一下节点的数量,6010、6011、6012已经下线了,缩容完成

[root@caochenlei redis-5.0.9]# /usr/local/redis/bin/redis-cli -h 192.168.190.128 -p 6001 -a 123456 -c

image-20200817230419019

第十章 Redis集群演变过程

第一阶段:单机版本

  • 优点:部署简单,使用方便
  • 缺点:采用单个Redis节点部署架构,没有备用节点实时同步数据,不提供备份策略,容灾性较差

第二阶段:主从复制

  • 优点: 主从模型可以提高读的能力,在一定程度上缓解了写的能力,实现了读写分离
  • 缺点:主节点宕机了,整个集群就没有可写的节点了

第三阶段:哨兵模式

  • 优点:哨兵模式可以提高读的能力,在一定程度上缓解了写的能力,并且主机故障确实能实现自动故障切换,提供稳定的服务
  • 缺点:仍然只有一个主节点,当并发写请求较大时,哨兵模式并不能缓解写压力

第四阶段:高可用集群

  • 优点:通过高可用集群,解决了写操作无法负载均衡以及存储能力受到单机限制的问题,实现了较为完善的高可用方案
  • 缺点:在select命令页有提到,集群模式下只能使用一个库,虽然平时一般也是这么用的,但是要了解一下

第十一章 Jedis连接Redis

环境准备:

第一步:创建一个名字叫JedisDemo的Java工程

第二步:创建一个lib文件夹,添加jedis-2.9.0.jar,选中右键Build Path

第三步:创建一个包com.caochenlei.test放在src文件夹中

第四步:选中工程名右键,Build Path > Add Libraries > Junit > Junit 4,添加一个单元测试

常见错误:

  • redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
    • 可能原因一:Redis的配置文件中没有注释掉bind 127.0.0.1
    • 可能原因二:Linux的防火墙没有关闭,CentOS6使用这两个命令: service iptables stop、chkconfig iptables off
    • 可能原因三:代码中的IP地址和端口写错了,请仔细检查一下!
  • redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required.
    • 可能原因一:出现这个说明Redis登录需要密码,而你的代码文件中并没有指明密码是多少,Redis登录是否启用密码请在配置文件中配置requirepass

11.1、Jedis连接单机版Redis

RedisSingle.java(全路径:/JedisDemo/src/com/caochenlei/test/RedisSingle.java)

@Test
public void testLink() {
	// 创建连接
	Jedis jedis = new Jedis("192.168.190.128", 6379);
	jedis.auth("123456");// 设置密码

	// 写入数据
	jedis.set("username", "zhangsan");

	// 读取数据
	System.out.println(jedis.get("username"));

	// 释放资源
	jedis.close();
}

11.2、Jedis连接主从版Redis

RedisCopy.java(全路径:/JedisDemo/src/com/caochenlei/test/RedisCopy.java)

@Test
public void testLink() {
	// 创建对应连接
	Jedis jedis_M = new Jedis("192.168.190.128", 8001);
	jedis_M.auth("123456");// 设置密码
	Jedis jedis_S1 = new Jedis("192.168.190.128", 8002);
	jedis_S1.auth("123456");// 设置密码
	Jedis jedis_S2 = new Jedis("192.168.190.128", 8003);
	jedis_S2.auth("123456");// 设置密码

	// 从机连接主机
	jedis_S1.slaveof("192.168.190.128", 8001);
	jedis_S2.slaveof("192.168.190.128", 8001);

	// 主机写入数据
	jedis_M.set("username", "zhangsan");

	// 从机读取数据
	System.out.println(jedis_S1.get("username"));
	System.out.println(jedis_S2.get("username"));

	// 释放所有资源
	jedis_S2.close();
	jedis_S1.close();
	jedis_M.close();
}

11.3、Jedis连接哨兵版Redis

注意:Java连接Redis Sentinel,哨兵模式下不要直连master,要连哨兵,master挂了才会自动故障转移,又因为连接哨兵需要连接池,所以我们需要在引入两个jar包,分别是:commons-pool2-2.4.2.jar、commons-lang3-3.3.2.jar,为了可以演示自动故障转移,您可以正常的启动一主两从和三个哨兵,然后执行以下代码,看一下效果,然后使用kill命令杀掉master,重新把set一个别的值,你再执行一次代码,你会发现set和get操作还是正常的,不会因为之前主节点宕机就不能工作,这就是哨兵模式的好处。

RedisSentinel.java(全路径:/JedisDemo/src/com/caochenlei/test/RedisSentinel.java)

@Test
public void testLink() {
	// 账户密码
	String masterName = "mymaster";
	String password = "123456";

	// 设置参数
	JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
	jedisPoolConfig.setMaxTotal(10);
	jedisPoolConfig.setMaxIdle(5);
	jedisPoolConfig.setMinIdle(5);

	// 哨兵信息
	Set<String> sentinels = new HashSet<String>();
	sentinels.add("192.168.190.128:26379");
	sentinels.add("192.168.190.128:26380");
	sentinels.add("192.168.190.128:26381");

	// 创建连接池
	JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels, jedisPoolConfig, password);

	// 获取客户端
	Jedis jedis = pool.getResource();

	// 执行写操作
	jedis.set("username", "lisi");

	// 执行读操作
	System.out.println(jedis.get("username"));

	// 释放资源
	pool.close();
}

11.4、Jedis连接集群版Redis

RedisCluster.java(全路径:/JedisDemo/src/com/caochenlei/test/RedisCluster.java)

private static JedisCluster jedis = null;

static {
	try {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxTotal(1024);
		jedisPoolConfig.setMaxIdle(200);
		jedisPoolConfig.setMaxWaitMillis(1000);
		jedisPoolConfig.setTestOnBorrow(true);
		jedisPoolConfig.setTestWhileIdle(true);
		jedisPoolConfig.setTestOnReturn(true);

		Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6001));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6002));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6003));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6004));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6005));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6006));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6007));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6008));
		jedisClusterNodes.add(new HostAndPort("192.168.190.128", 6009));

		jedis = new JedisCluster(jedisClusterNodes, 1000, 1000, 5, "123456", jedisPoolConfig);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

private static JedisCluster getJedis() {
	return jedis;
}

@Test
public void testLink() {
	try {
		// 获取链接
		JedisCluster jedisCluster = getJedis();

		// 执行写操作
		jedisCluster.set("username", "wangwu");

		// 执行读操作
		System.out.println(jedisCluster.get("username"));

		// 释放资源
		jedisCluster.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

第十二章 Jedis常用API

采用的是单机版的Redis,即一个Redis进行的测试。

12.1、string操作

JedisDemo1.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo1.java)

package com.caochenlei.demo;

import java.util.List;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.Jedis;

//string操作
public class JedisDemo1 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 set get
	@Test
	public void test1() {
		jedis.set("username", "tom");
		String value = jedis.get("username");
		System.out.println(value);
	}

	// 演示 mset mget
	@Test
	public void test2() {
		jedis.mset("password", "123", "age", "20");
		List<String> values = jedis.mget("username", "password", "age");
		System.out.println(values);
	}

	// 演示 append setrange getrange
	@Test
	public void test3() {
		jedis.append("username", " is boy");
		System.out.println(jedis.get("username"));
		jedis.setrange("username", 7, "girl");
		System.out.println(jedis.get("username"));
		System.out.println(jedis.getrange("username", 7, -1));
	}

}

12.2、list操作

JedisDemo2.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo2.java)

package com.caochenlei.demo;

import java.util.List;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.BinaryClient.LIST_POSITION;
import redis.clients.jedis.Jedis;

//list操作
public class JedisDemo2 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 lpush lrange
	@Test
	public void test1() {
		jedis.lpush("names", "tom", "james", "张三", "李四");
		List<String> names = jedis.lrange("names", 0, -1);
		System.out.println(names);
	}

	// 演示 lset lindex
	@Test
	public void test2() {
		jedis.lset("names", 1, "王五");
		List<String> names = jedis.lrange("names", 0, -1);
		System.out.println(names);

		String value = jedis.lindex("names", 1);
		System.out.println(value);
	}

	// 演示 linsert
	@Test
	public void test3() {
		jedis.linsert("names", LIST_POSITION.BEFORE, "james", "fox");
		List<String> names = jedis.lrange("names", 0, -1);
		System.out.println(names);
	}

	// 演示 lrem
	@Test
	public void test4() {
		jedis.lrem("names", 1, "tom");
		List<String> names = jedis.lrange("names", 0, -1);
		System.out.println(names);
	}

}

12.3、hash操作

JedisDemo3.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo3.java)

package com.caochenlei.demo;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.Jedis;

//hash操作
public class JedisDemo3 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 hset hget
	@Test
	public void test1() {
		jedis.hset("user", "username", "tom");
		String value = jedis.hget("user", "username");
		System.out.println(value);
	}

	// 演示 hmset hmget
	@Test
	public void test2() {
		Map<String, String> hash = new HashMap<String, String>();
		hash.put("password", "123");
		hash.put("sex", "male");
		jedis.hmset("user", hash);

		List<String> values = jedis.hmget("user", "username", "password", "sex");
		System.out.println(values);
	}

	// 演示 hgetall hkeys kvals
	@Test
	public void test3() {
		Map<String, String> map = jedis.hgetAll("user");
		for (String key : map.keySet()) {
			System.out.println(key + "  " + map.get(key));
		}

		Set<String> keys = jedis.hkeys("user");
		System.out.println(keys);

		List<String> values = jedis.hvals("user");
		System.out.println(values);
	}

	// 演示 hdel
	@Test
	public void test4() {
		jedis.hdel("user", "username", "password");
		Map<String, String> map = jedis.hgetAll("user");
		for (String key : map.keySet()) {
			System.out.println(key + "  " + map.get(key));
		}
	}

}

12.4、set操作

JedisDemo4.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo4.java)

package com.caochenlei.demo;

import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.Jedis;

//set操作
public class JedisDemo4 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 sadd smembers
	@Test
	public void test1() {
		jedis.sadd("language1", "java", "c++", "ruby", "python");
		Set<String> smembers = jedis.smembers("language1");
		System.out.println(smembers);
	}

	// 演示 srem
	@Test
	public void test2() {
		jedis.srem("language1", "java");
		Set<String> smembers = jedis.smembers("language1");
		System.out.println(smembers);
	}

	// 差集 sdiff
	@Test
	public void test3() {
		jedis.sadd("language1", "java", "c++", "ruby", "python");
		jedis.sadd("language2", "ios", "c++", "c#", "android");

		Set<String> sdiff = jedis.sdiff("language1", "language2");
		System.out.println(sdiff);
	}

	// 交集 sinter
	@Test
	public void test4() {
		jedis.sadd("language1", "java", "c++", "ruby", "python");
		jedis.sadd("language2", "ios", "c++", "c#", "android");
		Set<String> sinter = jedis.sinter("language1", "language2");
		System.out.println(sinter);
	}

	// 并集 sunion
	@Test
	public void test5() {
		jedis.sadd("language1", "java", "c++", "ruby", "python");
		jedis.sadd("language2", "ios", "c++", "c#", "android");
		Set<String> sunion = jedis.sunion("language1", "language2");
		System.out.println(sunion);
	}

}

12.5、sorted-set操作

JedisDemo5.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo5.java)

package com.caochenlei.demo;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;

//sorted-set操作
public class JedisDemo5 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 zadd zrange zrangeByScore
	@Test
	public void test1() {
		Map<String, Double> sm = new HashMap<String, Double>();
		sm.put("张三", 70.0);
		sm.put("李四", 80.0);
		sm.put("王五", 90.0);
		sm.put("赵六", 60.0);
		jedis.zadd("zkey", sm);

		Set<String> set = jedis.zrange("zkey", 0, -1);
		System.out.println(set);

		Set<String> set1 = jedis.zrangeByScore("zkey", 70.0, 90.0);
		System.out.println(set1);
	}

	// 演示 zrangeWithScores
	@Test
	public void test2() {
		Map<String, Double> sm = new HashMap<String, Double>();
		sm.put("张三", 70.0);
		sm.put("李四", 80.0);
		sm.put("王五", 90.0);
		sm.put("赵六", 60.0);
		jedis.zadd("zkey", sm);

		Set<Tuple> zws = jedis.zrangeWithScores("zkey", 0, -1);
		for (Tuple t : zws) {
			System.out.println(t.getScore() + "  " + t.getElement());
		}
	}

	// 演示 zrank
	@Test
	public void test3() {
		Map<String, Double> sm = new HashMap<String, Double>();
		sm.put("张三", 70.0);
		sm.put("李四", 80.0);
		sm.put("王五", 90.0);
		sm.put("赵六", 60.0);
		jedis.zadd("zkey", sm);

		Long num = jedis.zrank("zkey", "赵六");
		System.out.println(num);
	}

	// 演示 zscore
	@Test
	public void test4() {
		Map<String, Double> sm = new HashMap<String, Double>();
		sm.put("张三", 70.0);
		sm.put("李四", 80.0);
		sm.put("王五", 90.0);
		sm.put("赵六", 60.0);
		jedis.zadd("zkey", sm);
		Double zscore = jedis.zscore("zkey", "张三");
		System.out.println(zscore);
	}

	// 演示 zrem
	@Test
	public void test5() {
		Map<String, Double> sm = new HashMap<String, Double>();
		sm.put("张三", 70.0);
		sm.put("李四", 80.0);
		sm.put("王五", 90.0);
		sm.put("赵六", 60.0);
		jedis.zadd("zkey", sm);

		jedis.zrem("zkey", "李四");

		Set<Tuple> zws = jedis.zrangeWithScores("zkey", 0, -1);
		for (Tuple t : zws) {
			System.out.println(t.getScore() + "  " + t.getElement());
		}
	}

}

12.6、key操作

JedisDemo6.java(全路径:/JedisDemo/src/com/caochenlei/demo/JedisDemo6.java)

package com.caochenlei.demo;

import java.util.Set;

import org.junit.Before;
import org.junit.Test;

import redis.clients.jedis.Jedis;

//key操作
public class JedisDemo6 {
	Jedis jedis;

	@Before
	public void createJedis() {
		jedis = new Jedis("192.168.190.128");
		jedis.auth("123456");
	}

	// 演示 keys patten
	@Test
	public void test1() {
		Set<String> keys = jedis.keys("*");
		System.out.println(keys);
	}

	// 演示 del key
	@Test
	public void test2() {
		Long del = jedis.del("user");
		System.out.println(del);
	}

	// 演示 expire ttl
	@Test
	public void test3() {
		jedis.expire("username", 200);
		Long ttl = jedis.ttl("username");
		System.out.println(ttl);
	}

}

猜你喜欢

转载自blog.csdn.net/qq_38490457/article/details/108067929