Redis存储系统
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。拥有丰富的支持主流语言的客户端,C、C++、Python、Erlang、R、C#、Java、PHP、ObjectiveC、Perl、Ruby、Scala、Go、JavaScript。
Redis的特点
1、数据结构丰富
Redis 虽然也是键值对
数据库,但是和 Memcached 不同的是,Redis 的值
支持多种类型的数据结构,不仅可以是字符串,同时还提供散列(hash),列表(list),集合(sets),有序集合(sorted sets)等数据结构。
通过选用不同的数据结构,用户可以使用 Redis 解决各式各样的问题。
数据库有两种,一种硬盘数据库,一种内存数据库。
硬盘是把值储存在硬盘,典型的是 SQL 数据库。在内存里面就存储一下索引,当硬盘数据库想访问硬盘的值
时,它先在内存里面找到索引,然后在找值,问题是什么,在读取和写入硬盘的时候,如果读写比较多的时候,它会把硬盘 IO 堵死。
至于内存数据库,它会直接把值放到内存里面,内存数据库就直接把值取到,风一样的感觉,读写数据的时候都不会受到硬盘 IO 速度的限制,所以速度极快。
2、数据的持久化
Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
3、数据的备份
Redis 支持数据的备份,即 master-slave 模式的数据备份。
Redis的安装
下载安装包,redis-3.2.9.tar.gz 网址:http://www.redis.cn/download.html
第一步:依赖软件安装 yum -y install gcc tcl -y
第二步: 进入解压 redis 的文件夹
第三步: 执行 mkae 命令
第四步: 执行 make install
第五步: 修改 redis 的配置文件 redis.config ( 先备份一个原厂配置文件)
进入 myredis 文件中修改运行模式为后台运行,daemonize 修改成 yes
第六步:启动 redis 服务器,使用修改后配置文件的位置
命令: redis-server 配置文件的地址
如果修改 redis.conf,采用 redis 默认的 redis.conf 文件,redis 默认只能通过 127.0.0.1:6379 这个地址访问,这样就只能在本机上操作了,如果想要远程操作就不可行了。
这里需要修改 redis.conf 这个配置文件,在配置文件中添加相应的 ip 地址,这里假如添加 ip 地址:192.168.110.4,只需在 redis.conf 这个配置文件中 bind 127.0.0.1 后面追加:
bind 127.0.0.1 192.168.110.4 |
第七步:客户端连接:在命令行 输入 cd /usr/local/bin
进入这个目录 命令行输入: ./redis-cli
客户端命令格式:redis-cli –h host –p port
Redis的使用
Redis key 值是二进制安全的,这意味着可以用任何二进制序列作为 key值,从形如”foo”的简单字符串到一个 JPEG 文件的内容都可以。空字符串也是有效 key 值。
切换数据库
select databaseid 默认共有 16 个实例库,登录时是 ID 为 0 的数据库,总共有 16 个
Key操作
keys pattern
查找所有符合给定模式 pattern 的 key 。
exists key
检查给定 key 是否存在。
expire key seconds
为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
move key db
将当前数据库的 key 移动到给定的数据库 db 当中。如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。
ttl key
以秒为单位,返回给定 key 的剩余生存时间
type key
返回 key 所储存的值的类型。
del key [key …]
删除给定的一个或多个 key 。不存在的 key 会被忽略。
String操作
字符串是一种最基本的 Redis 值类型。Redis 字符串是二进制安全的,这意味着一个 Redis 字符串能包含任意类型的数据。
set key value [EX seconds] [PX milliseconds] [NX|XX]
- EX 设置过期时间,秒,等同于 SETEX key seconds value
- PX 设置过期时间,毫秒,等同于 PSETEX key milliseconds value
- NX 键不存在,才能设置,等同于 SETNX key value
- XX 键存在时,才能设置
将字符串值 value 关联到 key 。
如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
get key
返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 nil 。
假如 key 储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值。
append key value
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
strlen key
返回 key 所储存的字符串值的长度。
当 key 储存的不是字符串值时,返回一个错误。
incr key
将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
incrby key increment
将 key 所储存的值加上增量 increment 。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行INCRBY 命令。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
decr key
将 key 中储存的数字值减一。
decrby key decrement
将 key 所储存的值减去减量 decrement 。
getrange key start end
返回 key 中字符串值的子字符串。
字符串的截取范围由 start 和end 两个偏移量决定(包括 start 和 end 在内)。
负数偏移量表示从字符串最后开始计数, -1 表示最后一个字符, -2 表示倒数第二个,以此类推。
setrange key offset value
用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。
不存在的 key 当作空白字符串处理。
setex key seconds value
将值 value 关联到 key ,并将 key 的生存时间设为 seconds。
如果 key 已经存在, SETEX 命令将覆写旧值。
这个命令类似于以下两个命令:SET key value
expire key seconds # 设置生存时间
不同之处是, SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。
setnx key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。
mget key [key …]
返回所有(一个或多个)给定 key 的值。
如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。
mset key value [key value …]
同时设置一个或多个 key-value 对。
如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。
msetnx key value [key value …]
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
即使只有一个给定 key 已存在, MSETNX 也会拒绝执行所有给定 key 的设置操作。
MSETNX 是原子性的,因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object),所有字段要么全被设置,要么全不被设置。
List操作
lpush key value [value …]
将一个或多个值 value 插入到列表 key 的表头
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头 : 比如说 , 对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地 执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。
rpush key value [value …]
将一个或多个值 value 插入到列表 key 的表尾(最右边)。
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾 : 比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。
lrange key start stop
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
lpop key
移除并返回列表 key 的头元素。
rpop key
移除并返回列表 key 的尾元素。
lindex key index
返回列表 key 中,下标为 index 的元素。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
llen key
返回列表 key 的长度。
如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
lrem key count value
根据参数 count 的值,移除列表中与参数 value 相等的元素。
count 的值可以是以下几种:
count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
count = 0 : 移除表中所有与 value 相等的值。
ltrim key start stop
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
举个例子 , 执行命令 LTRIM list 0 2 , 表示只保留列表 list 的前三个元素,其余元素全部删除。
rpoplpush source destination
命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。
将 source 弹出的元素插入到列表 destination,作为 destination 列表的的头元素。
举个例子,你有两个列表 source 和 destination,source 列表有元素 a, b, c , destination 列表有元素 x, y, z ,执行 RPOPLPUSH sourcedestination 之后,source 列表包含元素 a, b , destination 列表包含元素 c, x, y, z ,并且元素 c 会被返回给客户端。
lset key index value
将列表 key 下标为 index 的元素的值设置为 value 。
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
linsert key BEFORE|AFTER pivot value
将值 value 插入到列表 key 当中,位于值 pivot 之前或之后。
当 pivot 不存在于列表 key 时,不执行任何操作。
当 key 不存在时, key 被视为空列表,不执行任何操作。
Set操作
sadd key member [member …]
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
smembers key
返回集合 key 中的所有成员。
不存在的 key 被视为空集合。
sismember key member
判断 member 元素是否集合 key 的成员。
scard key
返回集合 key 的基数(集合中元素的数量)。
srem key member [member …]
移除集合 key 中的一个或多个 member 元素 , 不存在的 member 元素会被忽略。
spop key (抽奖场景)
移除并返回集合中的一个随机元素。
如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。
smove source destination member
将 member 元素从 source 集合移动到 destination 集合。
SMOVE 是原子性操作。
如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 0 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去。
当 destination 集合已经包含 member 元素时,SMOVE命令只是简单地将 source 集合中的 member 元素删除。
当 source 或 destination 不是集合类型时,返回一个错误。
sdiff key [key …]
求差集:从第一个 key 的集合中去除其他集合和自己的交集部分
sinter key [key …] (微博求共同关注场景)
返回一个集合的全部成员,该集合是所有给定集合的交集。
不存在的 key 被视为空集。
当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。
sunion key [key …]
返回一个集合的全部成员,该集合是所有给定集合的并集。
不存在的 key 被视为空集。
Sorted set 操作
类似 Sets,但是每个字符串元素都关联到一个叫 score 浮动数值。里面的元素总是通过 score 进行着排序,所以不同的是,它是可以检索的一系列元素。注意:在 set 基础上,加上 score 值,之前 set 是key value1 value2….
现在 Zset 是 key score1 value1 score2 value2
zadd key score member [[score member] [score member] …]
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zrange key start stop [WITHSCORES]
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递减(从小到大)来排列。
zrevrange key start stop [WITHSCORES]( 音乐排行榜场景)
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递减(从大到小)来排列。
zrem key member [member …]
移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
zremrangebyscore key min max
移除有序集 key 中,所有 score 值介于 min 和 max 之间,(包括等于 min 或 max)的成员。
zscore key member
返回有序集 key 中,成员 member 的 score 值。
zcard key
返回有序集 key 的基数。
zcount key min max
返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max)的成员的数量。
zrank key member
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
Hash操作(散列)
KV 模式不变,但是 V 是一个键值对。
hset key field value
将哈希表 key 中的域 field 的值设为 value 。
hget key field
返回哈希表 key 中给定域 field 的值。
hmset key field value [field value …]
同时将多个 field-value (域-值)对设置到哈希表 key 中。
此命令会覆盖哈希表中已存在的域。
hmget key field [field …]
返回哈希表 key 中,一个或多个给定域的值。
hgetall key
返回哈希表 key 中,所有的域和值。
hkeys key
返回哈希表 key 中的所有域。
hvals key
返回哈希表 key 中所有域的值。
hsetnx key field value
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。
若域 field 已经存在,该操作无效。
hexists key field
查看哈希表 key 中,给定域 field 是否存在。
hdel key field [field …]
删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
hincrby key field increment
为哈希表 key 中的域 field 的值加上增量 increment 。
增量也可以为负数,相当于对给定域进行减法操作。
hincrbyfloat key field increment
增加浮点数。
场景:用户维度统计
统计数包括:关注数、粉丝数、喜欢商品数、发帖数
用户为 Key,不同维度为 Field,Value 为统计数。
Redis的持久化
Redis 提供了多种不同级别的持久化方式。
- RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
- AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
- Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
- 你甚至可以关闭持久化功能,让数据只在服务器运行时存在。
RDB(Redis DataBase)
Rdb:在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的 snapshot 快照,它恢复时就是将快照文件直接读到内存里。
Redis 会单独的创建(fork) 一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化的文件
。整个过程中,主进程是不进行任何 IO 操作,这就确保了极高的性能,如果需要进行大规模的数据恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方法要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。
Fork 的作用是复制一个与当前进程一样的进程,新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
Rdb 保存的是 dump.rdb 文件
RDB save 操作
Rdb 是整个内存的压缩的 snapshot,RDB 的数据结构,可以配置符合快照触发条件,默认的是 1 分钟内改动 1 万次,或者 5 分钟改动 10 次,或者是 15 分钟改动一次。
Save 禁用:如果想禁用 RDB 持久化的策略,只要不设置任何save 指令,或者是给 save 传入一个空字符串参数也可以。
save 指令:即刻保存操作对象
手动触发 RDB 快照
Save:
save 时只管保存,其他不管,全部阻塞。
Bgsave:
redis 会在后台进行快照操作,快照操作的同时还可以响应客户端的请求,可以通过 lastsave 命令获取最后一次成功执行快照的时间。
如何停止
静态停止:将配置文件里的 RDB 保存规则改为 save “”
动态停止 :
AOF(Append Only File)
以日志的形式来记录每个写操作,将 redis 执行过的所有写指令记录下来(读操作不记录)。只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次完成数据恢复工作。
=== APPEND ONLY MODE ==
开启 aof :appendonly yes (默认是 no)
AOF 策略
Appendfsync 参数:
**Always: **每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性较好。
Everysec: 出厂默认推荐,异步操作,每秒记录,不到一秒宕机,有数据丢失
**No:**从不 fsync ,将数据交给操作系统来处理。更快,也更不安全的选择。因为操作系统只会等内存满了才会溢写,如果没满宕机,数据就会丢失。
Rewrite概念:AOF 采用文件追加方式,文件会越来越来大为避免出现此种情况,新增了重写机制,aof 文件的大小超过所设定的阈值时,redis 就会自动 aof 文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令 bgrewirteaof。
**重写原理:**aof 文件持续增长而大时,会 fork 出一条新进程来将文件重写(也就是先写临时文件最后再 rename),遍历新进程的内存中的数据,每条记录有一条 set 语句,重写 aof 文件的操作,并没有读取旧的的 aof 文件,而是将整个内存的数据库内容用命令的方式重写了一个新的 aof 文件,这点和快照有点类似。
**触发机制:**redis 会记录上次重写的 aof 的大小,默认的配置当 aof 文件为上次 rewrite 后大小的一倍且文件大于 64M 触发。
no-appendfsync-on-rewrite no : 重写时是否可以运用 Appendfsync 用默认no 即可,保证数据安全
auto-aof-rewrite-percentage: 倍数 设置基准值
auto-aof-rewrite-min-size: 设置基准值大小
AOF 优点
使用 AOF 持久化会让 Redis 变得非常耐久:你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。
AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等),redis-check-aof 工具也可以轻易地修复这种问题。
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写,那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF 缺点
对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
备份 Redis 数据
一定要备份你的数据库!
磁盘故障, 节点失效, 诸如此类的问题都可能让你的数据消失不见, 不进行备份是非常危险的。
Redis 对于数据备份是非常友好的, 因为你可以在服务器运行的时候对RDB 文件进行复制: RDB 文件一旦被创建, 就不会进行任何修改。 当服务器要创建一个新的 RDB 文件时, 它先将文件的内容保存在一个临时文件里
面, 当临时文件写入完毕时, 程序才使用 rename(2) 原子地用临时文件替换原来的 RDB 文件。
这也就是说, 无论何时, 复制 RDB 文件都是绝对安全的。
建议:
创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。
至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。
Redis主从复制
Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(masterserver)的精确复制品 。
以下是关于 Redis 复制功能的几个重要方面:
-
Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
-
一个主服务器可以有多个从服务器。
-
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器,多个从服务器之间可以构成一个图状结构。
-
复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
-
复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步, 服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability): 比如说, 繁重的 SORT 命令可以交给附属节点去运行。
从服务器配置
配置一个从服务器非常简单, 只要在配置文件中增加以下的这一行就可以了:
slaveof 192.168.110.4 6379 |
另外一种方法是调用 SLAVEOF 命令,输入主服务器的 IP 和端口,然后同步就会开始
127.0.0.1:6379> SLAVEOF 192.168.110.4 6379 |
只读从服务器
从 Redis 2.6 开始, 从服务器支持只读模式, 并且该模式为从服务器的默认模式。
- 只读模式由 redis.conf 文件中的 slave-read-only 选项控制, 也可以通过 CONFIG SET 命令来开启或关闭这个模式。
- 只读从服务器会拒绝执行任何写命令, 所以不会出现因为操作失误而将数据不小心写入到了从服务器的情况。
- 另外,对一个从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。
- 利用『 SLAVEOF NO ONE 不会丢弃同步所得数据集』这个特性,可以在主服务器失败的时候,将从属服务器用作新的主服务器,从而实现无间断运行。
从服务器相关配置:
如果主服务器通过 requirepass 选项设置了密码, 那么为了让从服务器的同步操作可以顺利进行, 我们也必须为从服务器进行相应的身份验证设置。
对于一个正在运行的服务器, 可以使用客户端输入以下命令:
config set masterauth < password >
要永久地设置这个密码, 那么可以将它加入到配置文件中:
masterauth < password >
主服务器只在有至少 N 个从服务器的情况下,才执行写操作
从 Redis 2.8 开始, 为了保证数据的安全性,可以通过配置, 让主服务器只在有至少 N 个当前已连接从服务器的情况下, 才执行写命令。
不过, 因为 Redis 使用异步复制, 所以主服务器发送的写数据并不一定会被从服务器接收到, 因此, 数据丢失的可能性仍然是存在的。
以下是这个特性的运作原理:
- 从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理情况。
- 主服务器会记录各个从服务器最后一次向它发送 PING 的时间。
- 用户可以通过配置, 指定网络延迟的最大值 min-slaves-max-lag , 以及执行写操作所需的至少从服务器数量 min-slaves-to-write 。
如果至少有 min-slaves-to-write 个从服务器, 并且这些服务器的延迟值都少于 min-slaves-max-lag 秒, 那么主服务器就会执行客户端请求的写操作。
另一方面, 如果条件达不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的条件, 那么写操作就不会被执行, 主服务器会向请求执行写操作的客户端返回一个错误。
以下是这个特性的两个选项和它们所需的参数:
min-slaves-to-write < number of slaves >
min-slaves-max-lag < number of seconds >
Redis-sentinel( 哨兵)
Redis 的 Sentinel 系统用于管理多个 Redis 服务器,该系统执行以下三个任务:
- 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
- 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
- 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为新主服务器的从服务器; 当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效的主服务器。
Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 Sentinel 进程, 这些进程使用流言协议(gossipprotocols) 来接收关于主服务器是否下线的信息, 并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
虽然 Redis Sentinel 释出为一个单独的可执行文件 redis-sentinel , 但实际上它只是一个运行在特殊模式下的 Redis 服务器, 你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动 Redis Sentinel 。
启动 Sentinel
对于 redis-sentinel 程序, 可以用以下命令来启动Sentinel 系统:
redis-sentinel /path/sentinel.conf |
对于 redis-server 程序, 你可以用以下命令来启动一个运行在 Sentinel 模式下的 Redis 服务器:
redis-server /path/sentinel.conf --sentinel |
两种方法都可以启动一个 Sentinel 实例。
启动 Sentinel 实例必须指定相应的配置文件, 系统会使用配置文件来保存 Sentinel 的当前状态, 并在 Sentinel 重启时通过载入配置文件来进行状态还原。如果启动 Sentinel 时没有指定相应的配置文件, 或者指定的配置文件不可写(not writable), 那么 Sentinel 会拒绝启动。
配置 Sentinel
Redis 源码中包含了一个名为 sentinel.conf 的文件,这个文件是一个带有详细注释的 Sentinel 配置文件示例。
运行一个 Sentinel 所需的最少配置如下所示:
# 制定了 Sentinel 去监视的主服务器名称、IP、端口、Sentinel投票个数 |
注意:主服务器无密码时,记得在 sentinel 配置里配上 bind 本机 ip ,或者关掉保护模式 protected-mode no
第一行配置指示 Sentinel 去监视一个名为 mymaster 的主服务器, 这个主服务器的 IP 地址为 127.0.0.1 , 端口
号为 6379 , 而将这个主服务器判断为失效至少需要 2 个Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
不过要注意, 无论你设置要多少个 Sentinel 同意才能判断一个服务器失效, 一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持, 才能发起一次自动故障迁移。换句话说, 在只有少数(minority) Sentinel 进程正常运作的情况下, Sentinel 是不能执行自动故障迁移的。
其他选项的基本格式如下:
sentinel <选项的名字> <主服务器的名字> <选项的值>
各个选项的功能如下:
- down-after-milliseconds 选项指定了 Sentinel 认为服务器已经断线所需的毫秒数。如果服务器在给定的毫秒数之内, 没有返回 Sentinel 发送的 PING 命令的回复, 或者返回一个错误, 那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN )。不过只有一个 Sentinel 将服务器标记为主观下线并不一定会引起服务器的自动故障迁移: 只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后, 服务器才会被标记为客观下线,这时自动故障迁移才会执行。将服务器标记为客观下线所需的 Sentinel 数量由对主服务器的配置决定。
- parallel-syncs 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步, 这个数字越小, 完成故障转移所需的时间就越长。如果从服务器被设置为允许使用过期数据集(参见对 redis.conf 文件中对 slave-serve-stale-data 选项的说明),那么你可能不希望所有从服务器都在同一时间向新的主服务器发送同步请求,
因为尽管复制过程的绝大部分步骤都不会阻塞从服务器, 但从服务器在载入主服务器发来的 RDB 文件时, 仍然会造成从服务器在一段时间内不能处理命令请求: 如果全部从服务器一起对新的主服务器进行同步, 那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。你可以通过将这个值设为 1 来保证每次只有一个从服务器处于不能处理命令请求的状态。
测试效果:
1、3台服务器已经都安装好Redis,并且做好主从复制。
2、分别在3台服务器中redis安装包中新建文件 mysentinel.conf,填入如上的 Sentinel 配置。
这里注意:守护进程一定要关掉。(3台都关)
3、分别启动3台服务器的redis-server,以及redis-sentinel。
4、关掉主服务器,查看其它服务的监听状态。
会发现30秒后,哨兵sentinel会重新选举新的master
192.168.110.5节点成为新的master。
注意:如果重新启动之前的主节点 192.168.110.4,那它依旧是slave,无法还原到master!
Redis集群
集群简介
Redis 集群可以在多个 Redis 节点之间进行数据共享。
Redis 集群不支持那些需要同时处理多个键的 Redis 命令,因为执行这些命令需要在多个 Redis 节点之间移动数据, 并且在高负载的情况下, 这些命令将降低 Redis 集群的性能, 并导致不可预测的行为。比如:有 name1 和 name2 两个节点,命令 del name1 name2 要同时删除这个 key,就不能写在一起,得分开写。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis 集群提供了以下两个好处:
- 将数据自动切分(split)到多个节点的能力。
- 当集群中的一部分节点失效或者无法进行通讯时, 仍然可以继续处理命令请求的能力。
集群数据共享
Redis 集群使用**数据分片(sharding)**而非一致性哈希来实现: 一个 Redis 集群包含 16384 个哈希槽(hash slot),数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16校验和 。
集群中的每个节点负责处理一部分哈希槽。 举个例子, 一个集群可以有三个哈希槽, 其中
- 节点 A 负责处理 0 号至 5500 号哈希槽。
- 节点 B 负责处理 5501 号至 11000 号哈希槽。
- 节点 C 负责处理 11001 号至 16384 号哈希槽。
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。 比如说:
- 如果用户将新节点 D 添加到集群中, 那么集群只需要将节点A 、B 、 C 中的某些槽移动到节点 D 就可以了。
- 与此类似, 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C , 然后再移除空白(不包含任何哈希槽)的节点 A 就可以了。
因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点, 又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
集群的主从复制
为了使得集群在一部分节点下线或者无法与集群的大多数节点进行通讯的情况下, 仍然可以正常运作, Redis 集群对节点使用了主从复制功能: 集群中的每个节点都有 1 个至 N 个复制品,其中一个复制品为主节点, 而其余的 N-1 个复制品为从节点。
在之前列举的节点 A 、B 、C 的例子中, 如果节点 B 下线了, 那么集群将无法正常运行, 因为集群找不到节点来处理 5501 号至 11000 号的哈希槽。
另一方面, 假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候, 集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5501 号至 11000 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。
不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
集群的一致性保证
Redis 集群不保证数据的强一致性(strong consistency):在特定条件下, Redis 集群可能会丢失已经被执行过的写命令。
使用异步复制是 Redis 集群可能会丢失写命令的其中一个原因。 考虑以下这个写命令的例子:
- 客户端向主节点 B 发送一条写命令。
- 主节点 B 执行写命令,并向客户端返回命令回复。
- 主节点 B 将刚刚执行的写命令复制给它的从节点 B1 、 B2 和B3 。
如你所见, 主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。
Redis 集群另外一种可能会丢失命令的情况是, 集群出现网络分裂(network partition), 并且一个客户端与至少包括一个主节点在内的少数(minority)实例被孤立。
举个例子, 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1六个节点, 其中 A 、B 、C 为主节点, 而 A1 、B1 、C1 分别为三个主节点的从节点, 另外还有一个客户端 Z1 。
假设集群中发生网络分裂, 那么集群可能会分裂为两方,大多数(majority)的一方包含节点 A 、C 、A1 、B1 和C1 , 而少数(minority)的一方则包含节点 B 和客户端Z1 。在网络分裂期间, 主节点 B 仍然会接受 Z1 发送的写命令:
- 如果网络分裂出现的时间很短, 那么集群会继续正常运行;
- 但是, 如果网络分裂出现的时间足够长, 使得大多数一方将从节点 B1 设置为新的主节点, 并使用 B1 来代替原来的主节点 B ,那么 Z1 发送给主节点 B 的写命令将丢失。
注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:
- 对于大多数一方来说, 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群, 那么集群会将这个主节点视为下线, 并使用从节点来代替这个主节点继续工作。
- 对于少数一方, 如果一个主节点未能在节点超时时间所设定的时限内重新联系上集群, 那么它将停止处理写命令, 并向客户端报告错误。
集群搭建
要让集群正常运作至少需要 3 个主节点, 不过在刚开始试用集群功能时, 强烈建议使用六个节点: 其中三个为主节点,而其余三个则是各个主节点的从节点。
1、集群规划:
3 个主节点,3 个从节点。
2、新建配置文件(3台都要)
-
在redis安装包中新建文件夹 redis-cluster
-
在 redis-cluster 中新建分别以端口7000和7001命名新建两个文件夹
-
分别在7000和7001两个文件夹中新建配置文件 redis.conf(端口号要与文件夹对应)
配置文件 redis.conf
port 7000/7001 |
3、启动 redis 服务
在每台机器的 7000 和 7001 文件夹下,使用命令 redis-server redis.conf
启动实例
4、安装 ruby 相关的程序
yum install ruby |
5、执行命令创建集群
在主服务器 redis 的安装包的 src 目录下执行以下命令:
./redis-trib.rb create --replicas 1 ip1:7000 ip1:7001 Ip2:7000 ip2:7001 ip3:7000 ip3:7001 |
可以看到,集群为每个节点分配了相应的插槽。输入 yes 即可完成集群搭建。
6、测试结果
输入命令:redis-cli,发现无法连接
这是因为 redis 客户端默认的连接端口是 6379,但现在集群用的是 7000 和 7001
所以我们加上端口,如:redis-cli -p 7000
但是当我们给 redis 设置值的时候会发现有的值会报错
由图中可以看出 name 这个 key 被移动到了 ip 为192.168.110.5的节点上,这是因为 name 在进行 hash 之后取到的插槽值就处于192.168.110.5这个节点。
所以我们可以到192.168.110.5这个节点去设置 key 为 name 的值。
但是这样切来切去有没有觉得很麻烦。Redis为我们提供了一种可以自动进行切换节点的方法,只需要在连接客户端时加上参数:-c
我们会发现节点自动切换到了192.168.110.5这个节点上,是不是很方便!