1.Redis(非关系数据库)
- Redis 是一个使用 C 语言写成的,开源的 key-value 数据库,与Memcached类似
- 它支持存储的value 类型相对更多, 包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)这些数据类型都支持 push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的
- 在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率, 数据都是缓存在内存中。区别是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并在此基础上实现了主从同步
特点:
- 速度快,redis虽然是单线程架构,但是由于redis的数据是运行在内存中的,所以速度非常快
- 支持丰富的数据类型,具有五大基本数据类型
- 支持事务
- 丰富的功能特性
redis单线程架构
- redis是单线程来处理命令的,所以一条命令从客户端到达服务端不会立刻被执行,所有命令都会进入一个队列中,然后被执行。当然发送命令、返回结果、命令排队并不是像排队那么简单, redis采用了I/O多路复用的技术来解决I/O的问题
既然是单线程架构为什么还这么快?
纯内存访问,Redis将所有数据放在内存中,内存响应时间大约为100纳秒。这是redis达到每秒万级别的访问的重要基础
非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现。加上Redis自身事件处理模型将epoll中的连接,读写,关闭都转换为事件,不在网络I/O上浪费过多时间
单线程避免了线程切换和竞态产生的消耗
客户端与服务端请求过程
- 所有命令在一个队列里等待被执行
Redis应用场景
- 建过期功能:缓存,session会话保存,优惠券国企
- 列表:排行榜
- 天然计数器:帖子浏览数、视频播放数、评论留言数
- 集合:兴趣标签,广告投放
- 消息队列:ELK
- 文章列表
安装部署
- 软件安装
[root@localhost ~]# yum install epel-release.noarch -y[root@localhost ~]# yum install redis -y
- 相关文件
[root@localhost ~]# rpm -ql redis/etc/redis.conf # 主配置文件/usr/bin/redis-benchmark # redis性能检测工具/usr/bin/redis-check-aof # AOF文件修复工具/usr/bin/redis-check-rdb # 文件检查工具/usr/bin/redis-cli # redis客户端/usr/bin/redis-server # redis服务端/usr/lib/systemd/system/redis.service # 守护进程/var/lib/redis # redis数据目录/var/log/redis # redis日志文件
- 配置文件介绍
[root@node1 ~]# cat /etc/redis.conf | grep -Ev "^$|^#"==================================基础配置=============================================bind 127.0.0.1 # 绑定IP地址protected-mode yesport 6379 # 监听端口tcp-backlog 511timeout 0 # 设置客户端连接超时时间tcp-keepalive 300 # 检测客户端是否健康的周期时间daemonize yes # 是否以守护进程方式启动supervised nopidfile /var/run/redis_6379.pid # PID文件loglevel notice # 日志等级logfile /var/log/redis/redis.log # 日志文件databases 16 # 设置数据库的数目==================================RDB触发条件===========================================save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yes # 当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据rdbcompression yes # 对于存储到磁盘中的快照,可以设置是否进行压缩存储rdbchecksum yes # 在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验dbfilename dump.rdb # 设置快照的文件名dir /var/lib/redis # 设置快照文件的存放路径==================================主从复制=============================================slave-serve-stale-data yesslave-read-only yes # 配置Redis的Slave实例是否接受写操作repl-diskless-sync no # 主从数据复制是否使用无硬盘复制功能repl-diskless-sync-delay 5 # 等待时间repl-disable-tcp-nodelay no # 同步之后是否禁用从站上的TCP_NODELAYslave-priority 100==================================AOF相关配置=============================================appendonly no # 默认redis使用的是rdb方式持久化appendfilename "appendonly.aof" # 文件名appendfsync everysec # aof持久化策略的配置no-appendfsync-on-rewrite no # 在aof重写或者写入rdb文件的时候,不执行持久化策略auto-aof-rewrite-percentage 100 # 当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写auto-aof-rewrite-min-size 64mb # 设置允许重写的最小aof文件大小aof-load-truncated yes # 当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。lua-time-limit 5000 # 一个lua脚本执行的最大时间
Redis启动
- 启动时使用redis-server,参数是配置文件(可以指定参数配置文件),也可以不指定使其默认加载)
[root@localhost ~]# redis-server /etc/redis.conf
- 登录使用redis-cli工具进行登录
[root@localhost ~]# redis-cli --helpredis-cli 3.2.12Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]-h <hostname> Server hostname (default: 127.0.0.1).-p <port> Server port (default: 6379).-s <socket> Server socket (overrides hostname and port).-a <password> Password to use when connecting to the server.-r <repeat> Execute specified command N times.-i <interval> When -r is used, waits <interval> seconds per command.It is possible to specify sub-second times like -i 0.1.-n <db> Database number.-x Read last argument from STDIN.-d <delimiter> Multi-bulk delimiter in for raw formatting (default: \n).-c Enable cluster mode (follow -ASK and -MOVED redirections).[root@localhost ~]# redis-cli -h 127.0.0.1 -p 6379127.0.0.1:6379>
- 关闭redis服务
127.0.0.1:6379> SHUTDOWNnot connected>
2.Redis基础命令
命令手册: http://redisdoc.com/index.html
全局命令
- KEY* :查看所有键,该命令在生产环境中慎用,会一次性读取所有键,可能会导致阻塞
127.0.0.1:6379> KEYS *1) "k3"2) "k1"3) "k4"4) "k2"
- dbsize:查看当前数据库中的所有键的数量
127.0.0.1:6379> DBSIZE(integer) 4
- exists key:检查某个键是否存在,存在返回1,不存在返回0
127.0.0.1:6379> EXISTS k1(integer) 1127.0.0.1:6379> EXISTS k5(integer) 0
- del key:删除键,可以只删除一个键也可以同时删除多个键,删除成功后会返回成功删除键的个 数,删除失败会返回0
127.0.0.1:6379> DEL k1 k4(integer) 2127.0.0.1:6379> keys *1) "k3"2) "k2"
- expire key seconds:给键设置过期时间,当超过过期时间后会自动删除键
ttl命令会返回键过期时间
- 大于等于0的整数,键剩余过期的时间
- -1:键没设置过期时间
- -2:键不存在
127.0.0.1:6379> EXPIRE k3 20(integer) 1127.0.0.1:6379> TTL k3(integer) 13127.0.0.1:6379> TTL k3(integer) -2127.0.0.1:6379> TTL k2(integer) -1127.0.0.1:6379> get k2"v2"127.0.0.1:6379> get k3(nil)
- 查看键的数据结构类型,数据结构类型有五大基本数据类型,现在以字符串为例
127.0.0.1:6379> TYPE k2string
字符串命令
- 字符串类型是redis最基础的数据结构,而且其他集中数据结构都是在字符串类型基础上构建的
- 设置值
127.0.0.1:6379>127.0.0.1:6379> SET key value [EX seconds] [PX milliseconds] [NX|XX]ex seconds:为键设置秒级过期时间px milliseconds:为键设置毫秒级过期时间nx:键必须不存在,才可以设置成功,用于添加xx:与nx相反,键必须存在,才可以设置成功,用于更新(setex key seconds value setnx key value它们的作用和ex和nx选项是一样的)当前键hello不存在:127.0.0.1:6379> exists hello(integer) 0设置键为hello,值为world的键值对:127.0.0.1:6379> set hello worldOK因为键hello已存在,所以setnx失败,返回结果为0:127.0.0.1:6379> setnx hello redis(integer) 0因为键hello已存在,所以set xx成功,返回结果为OK:127.0.0.1:6379> set hello jedis xxOK
- 获取值
下面操作获取键hello的值:127.0.0.1:6379> get hello"world"如果要获取的键不存在,则返回nil(空):127.0.0.1:6379> get not_exist_key(nil)
- 批量设置值
127.0.0.1:6379> mset a 1 b 2 c 3 d 4OK
- 批量获取值
127.0.0.1:6379> mget a b c d1) "1"2) "2"3) "3"4) "4"
- 计数
incr key
incr命令用于对值做自增操作,返回结果分为三种情况:
·值不是整数,返回错误。
·值是整数,返回自增后的结果。
·键不存在,按照值为0自增,返回结果为1。
例如对一个不存在的键执行incr操作后,返回结果是1:
127.0.0.1:6379> exists key
(integer) 0
127.0.0.1:6379> incr key
(integer) 1
再次对键执行incr命令,返回结果是2:
127.0.0.1:6379> incr key
(integer) 2
如果值不是整数,那么会返回错误:
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> incr hello
(error) ERR value is not an integer or out of range
- 不常用命令(扩展)
(1)追加值
append key value
append可以向字符串尾部追加值,例如:
127.0.0.1:6379> get key
"redis"
127.0.0.1:6379> append key world
(integer) 10
127.0.0.1:6379> get key
"redisworld"
(2)字符串长度
strlen key
例如,当前值为redisworld,所以返回值为10:
127.0.0.1:6379> get key
"redisworld"
127.0.0.1:6379> strlen key
(integer) 10
下面操作返回结果为6,因为每个中文占用3个字节:
83
127.0.0.1:6379> set hello "世界"
OK
127.0.0.1:6379> strlen hello
(integer) 6
(3)设置并返回原值
getset key value
getset和set一样会设置值,但是不同的是,它同时会返回键原来的值,
例如:
127.0.0.1:6379> getset hello world
(nil)
127.0.0.1:6379> getset hello redis
"world"
(4)设置指定位置的字符
setrange key offeset value
下面操作将值由pest变为了best:
127.0.0.1:6379> set redis pest
OK
127.0.0.1:6379> setrange redis 0 b
(integer) 4
127.0.0.1:6379> get redis
"best"
(5)获取部分字符串
getrange key start end
start和end分别是开始和结束的偏移量,偏移量从0开始计算,例如下面
84
操作获取了值best的前两个字符。
127.0.0.1:6379> getrange redis 0 1
"be"
列表命令
- 列表(list)类型是用来存储多个有序的字符串,是一种线性结构可以充当栈和队列的角色
- 添加操作
(1)从右边插入元素
rpush key value [value ...]
下面代码从右向左插入元素c、b、a:
127.0.0. 1:6379> rpush listkey c b a
(integer) 3
lrange0-1命令可以从左到右获取列表的所有元素:
127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "b"
3) "a"
108
(2)从左边插入元素
lpush key value [value ...]
使用方法和rpush相同,只不过从左侧插入,这里不再赘述。
(3)向某个元素前或者后插入元素
linsert key before|after pivot value
linsert命令会从列表中找到等于pivot的元素,在其前(before)或者后
(after)插入一个新的元素value,例如下面操作会在列表的元素b前插入
java:
127.0.0.1:6379> linsert listkey before b java
(integer) 4
返回结果为4,代表当前命令的长度,当前列表变为:
127.0.0.1:6379> lrange listkey 0 -1
1) "c"
2) "java"
3) "b"
4) "a"
- 查找
(1)获取指定范围内的元素列表
lrange key start end
lrange操作会获取列表指定索引范围所有的元素。索引下标有两个特点:第一,索引下标从左到右分别是0到
N-1,但是从右到左分别是-1到-N。
第二,lrange中的end选项包含了自身,这个和很多编程语言不包含end不太相同,例如想获取列表的第2到
第4个元素,可以执行如下操作:
127.0.0.1:6379> lrange listkey 1 3
1) "java"
2) "b"
3) "a"
(2)获取列表指定索引下标的元素
lindex key index
例如当前列表最后一个元素为a:
127.0.0.1:6379> lindex listkey -1
"a"
(3)获取列表长度
llen key
例如,下面示例当前列表长度为4:
127.0.0.1:6379> llen listkey
(integer) 4
- 删除
(1)从列表左侧弹出元素
lpop key
110
如下操作将列表最左侧的元素c会被弹出,弹出后列表变为java、b、
a:
127.0.0.1:6379>t lpop listkey
"c"
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
3) "a"
(2)从列表右侧弹出
rpop key
它的使用方法和lpop是一样的,只不过从列表右侧弹出,这里不再赘
述。
(3)删除指定元素
lrem key count value
lrem命令会从列表中找到等于value的元素进行删除,根据count的不同
分为三种情况:
·count>0,从左到右,删除最多count个元素。
·count<0,从右到左,删除最多count绝对值个元素。
·count=0,删除所有。
例如向列表从左向右插入5个a,那么当前列表变为“a a a a a java b a”,
下面操作将从列表左边开始删除4个为a的元素:
127.0.0.1:6379> lrem listkey 4 a
(integer) 4
127.0.0.1:6379> lrange listkey 0 -1
1) "a"
2) "java"
3) "b"
4) "a"
(4)按照索引范围修剪列表
ltrim key start end
例如,下面操作会只保留列表listkey第2个到第4个元素:
127.0.0.1:6379> ltrim listkey 1 3
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
3) "a"
- 修改
修改指定索引下标的元素:
lset key index newValue
下面操作会将列表listkey中的第3个元素设置为python:
127.0.0.1:6379> lset listkey 2 python
OK
127.0.0.1:6379> lrange listkey 0 -1
1) "java"
2) "b"
3) "python"
哈希命令
几乎所有编程语言都提供了hash类型,他们的叫法可能是哈希、字典、关联数组
常常会使用 hash数据类型存储关系型数据库中的表
设置值
# hset key field value
127.0.0.1:6379> hset user:1 name tom
(integer) 1
- 获取值
# hget key field
127.0.0.1:6379> hget user:1 name
"tom"
- 删除field
# hdel key field [field ...]
hdel会删除一个或多个field,返回结果为成功删除field的个数
127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hdel user:1 age
(integer) 0
- 计算field个数
# hlen key
127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379> hset user:1 age 23
(integer) 1
127.0.0.1:6379> hset user:1 city tianjin
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3
- 批量设置或获取field-value
# hmget key field [field ...] # hmset key field value [field value ...] hmset和hmget分别是批量设置和获取field-value,hmset需要的参数是key 和多对field-value,hmget需要的参数是key和多个field 127.0.0.1:6379> hmset user:1 name mike age 12 city tianjin OK 127.0.0.1:6379> hmget user:1 name city 1) "mike" 2) "tianjin"
- 判断field是否存在
# hexists key field 127.0.0.1:6379> hexists user:1 name (integer) 1
- 获取所有field
# hkeys key
hkeys命令应该叫hfields更为恰当,它返回指定哈希键所有的field
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "city"
- 获取所有value
# hvals key
下面操作获取user:1全部value:
127.0.0.1:6379> hvals user:1
1) "mike"
2) "12"
3) "tianjin"
- 获取所有的field-value
# hgetall key
下面操作获取user:1所有的field-value:
127.0.0.1:6379> hgetall user:1
1) "name"
2) "mike"
3) "age"
4) "12"
5) "city"
6) "tianjin"
集合命令
集合(set)类型也是用来保存多个的字符串元素,集合中不允许有重复出现的元素,集合的元素 是没有顺序的不能够通过下标来获得值
添加元素
# sadd key element [element ...]
返回结果为添加成功的元素个数
127.0.0.1:6379> exists myset
(integer) 0
127.0.0.1:6379> sadd myset a b c
(integer) 3
127.0.0.1:6379> sadd myset a b
(integer) 0
- 删除元素
# srem key element [element ...]
返回结果为成功删除元素个数
127.0.0.1:6379> srem myset a b
(integer) 2
127.0.0.1:6379> srem myset hello
(integer) 0
- 计算元素个数
# scard key
122
scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接用Redis内部的变量
127.0.0.1:6379> scard myset
(integer) 1
- 判断元素是否在集合中
# sismember key element
如果给定元素element在集合内返回1,反之返回0:
127.0.0.1:6379> sismember myset c
(integer) 1
- 随机从集合中返回指定元素个数
# srandmember key [count]
[count]是可选参数,如果不写默认为1
127.0.0.1:6379> srandmember myset 2
1) "a"
2) "c"
127.0.0.1:6379> srandmember myset
"d"
- 从集合随机弹出元素
# spop key
spop操作可以从集合中随机弹出一个元素,例如下面代码是一次spop后,集合元素变为"d b a":
127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
- 获取所有元素
# smembers key
下面代码获取集合myset所有元素,并且返回结果是无序的:
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
- 交集运算
# 127.0.0.1:6379> sadd user:1:follow it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2:follow it news ent sports
(integer) 4
sinter key [key ...]
例如下面代码是求user:1:follow和user:2:follow两个集合的交集,返回结果是sports、it:
127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"
- 并集运算
# suinon key [key ...]
例如下面代码是求user:1:follow和user:2:follow两个集合的并集,返回结果是sports、it、
his、news、music、ent:
127.0.0.1:6379> sunion user:1:follow user:2:follow
1) "sports"
2) "it"
3) "his"
4) "news"
5) "music"
6) "ent"
- 差集运算
# sdiff key [key ...]
例如下面代码是求user:1:follow和user:2:follow两个集合的差集,返回结果是music和his:
127.0.0.1:6379> sdiff user:1:follow user:2:follow
1) "music"
2) "his"
- 将交集、并集、差集的结果保存
# sinterstore destination key [key ...]
# suionstore destination key [key ...]
# sdiffstore destination key [key ...]
集合间的运算在元素较多的情况下会比较耗时,所以Redis提供了上面三个命令(原命令+store)将集合间交
集、并集、差集的结果保存在
destination key中,例如下面操作将user:1:follow和user:2:follow两个集合的交集结果保存在
user:1_2:inter中,user:1_2:inter本身也是集合类型:
127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> type user:1_2:inter
set
127.0.0.1:6379> smembers user:1_2:inter
1) "it"
2) "sports"
有序集合命令
有序集合保留了集合不能有重复成员的特性,不同的是有序集合中的元素是可以排序的。但是它和 列表使用索引下标作为排序依据不同的是它给每个元素设置一个分数(score)作为排序的依据
添加成员
# zadd key score member [score member ...]
下面操作向有序集合user:ranking添加用户tom和他的分数251:
127.0.0.1:6379> zadd user:ranking 251 tom
(integer) 1
127.0.0.1:6379> zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin
(integer) 5
有关zadd命令有两点需要注意:
Redis3.2为zadd命令添加了nx、xx、ch、incr四个选项:
nx:member必须不存在,才可以设置成功,用于添加。
xx:member必须存在,才可以设置成功,用于更新。
h:返回此次操作后,有序集合元素和分数发生变化的个数
incr:对score做增加,相当于后面介绍的zincrby。
- 计算成员个数
# zcard key
例如下面操作返回有序集合user:ranking的成员数为5,和集合类型的
scard命令一样,zcard的时间复杂度为O(1)。
127.0.0.1:6379> zcard user:ranking
(integer) 5
- 计算某个成员的分数
# zscore key member
tom的分数为251,如果成员不存在则返回nil:
127.0.0.1:6379> zscore user:ranking tom
"251"
127.0.0.1:6379> zscore user:ranking test
(nil)
- 计算某个成员的排名
# zrank key member
# zrevrank key member
zrank是从分数从低到高返回排名,zrevrank反之。例如下面操作中,tom
在zrank和zrevrank分别排名第5和第0(排名从0开始计算)。
127.0.0.1:6379> zrank user:ranking tom
(integer) 5
127.0.0.1:6379> zrevrank user:ranking tom
(integer) 0
- 删除成员
# zrem key member [member ...]
下面操作将成员mike从有序集合user:ranking中删除。
127.0.0.1:6379> zrem user:ranking mike
(integer) 1
返回结果为成功删除的个数。
- 增加成员的分数
# zincrby key increment member
下面操作给tom增加了9分,分数变为了260分:
127.0.0.1:6379> zincrby user:ranking 9 tom
"260"
- 返回指定排名范围的成员
# zrange key start end [withscores]
# zrevrange key start end [withscores]
有序集合是按照分值排名的,zrange是从低到高返回,zrevrange反之。
下面代码返回排名最低的是三个成员,如果加上withscores选项,同时会返
回成员的分数:
127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "frank"
4) "200"
5) "tim"
6) "220"
127.0.0.1:6379> zrevrange user:ranking 0 2 withscores
1) "tom"
2) "260"
3) "martin"
4) "250"
5) "tim"
6) "220"
- 返回指定分数范围的成员
# zrangebyscore key min max [withscores] [limit offset count]
# zrevrangebyscore key max min [withscores] [limit offset count]
其中zrangebyscore按照分数从低到高返回,zrevrangebyscore反之。例如
下面操作从低到高返回200到221分的成员,withscores选项会同时返回每个
成员的分数。[limit offset count]选项可以限制输出的起始位置和个数:
127.0.0.1:6379> zrangebyscore user:ranking 200 tinf withscores
1) "frank"
2) "200"
3) "tim"
4) "220"
127.0.0.1:6379> zrevrangebyscore user:ranking 221 200 withscores
1) "tim"
2) "220"
3) "frank"
4) "200"
同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和
+inf分别代表无限小和无限大:
127.0.0.1:6379> zrangebyscore user:ranking (200 +inf withscores
1) "tim"
2) "220"
3) "martin"
4) "250"
5) "tom"
6) "260"
- 返回指定分数范围的成员个数
# zcount key min max
下面操作返回200到221分的成员的个数:
127.0.0.1:6379> zcount user:ranking 200 221
(integer) 2
- 删除指定排名内的升序元素
# zremrangebyrank key start end
下面操作删除第start到第end名的成员:
127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3
- 删除指定分数范围的成员
# zremrangebyscore key min max
下面操作将250分以上的成员全部删除,返回结果为成功删除的个数:
127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf
(integer) 2
- 交集运算
127.0.0.1:6379> zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim 250 martin
251 tom
(integer) 6
127.0.0.1:6379> zadd user:ranking:2 8 james 77 mike 625 martin 888 tom
(integer) 4
zinterstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
这个命令参数较多,下面分别进行说明:
destination:交集计算结果保存到这个键。
numkeys:需要做交集计算键的个数。
key[key...]:需要做交集计算的键。
weights weight[weight...]:每个键的权重,在做交集计算时,每个键中
的每个member会将自己分数乘以这个权重,每个键的权重默认是1。
aggregate sum|min|max:计算成员交集后,分值可以按照sum(和)、
min(最小值)、max(最大值)做汇总,默认值是sum。
127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1
user:ranking:2
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "168"
3) "martin"
4) "875"
5) "tom"
6) "1139"
如果想让user:ranking:2的权重变为0.5,并且聚合效果使用max,可以
执行如下操作:
127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1
user:ranking:2 weights 1 0.5 aggregate max
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "91"
3) "martin"
4) "312.5"
5) "tom"
6) "444"
- 并集运算
# zunionstore destination numkeys key [key ...] [weights weight [weight ...]]
[aggregate sum|min|max]
该命令的所有参数和zinterstore是一致的,只不过是做并集计算,例如
下面操作是计算user:ranking:1和user:ranking:2的并集,weights和
aggregate使用了默认配置,可以看到目标键user:ranking:1_union_2对分值
做了sum操作:
127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1
user:ranking:2
(integer) 7
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
1) "kris"
2) "1"
3) "james"
4) "8"
5) "mike"
6) "168"
7) "frank"
8) "200"
9) "tim"
10) "220"
11) "martin"
12) "875"
13) "tom"
14) "1139"
3.Redis丰富特性
Redis除了提供五大基本数据类型,还提供了丰富的功能强大的附加功能
- 慢查询分析:通过慢查询分析,找到有问题的命令进行优化。
- Redis Shell:功能强大的Redis Shell会有意想不到的实用功能。
- Pipeline:通过Pipeline(管道或者流水线)机制有效提高客户端性能。
- 事务与Lua:制作自己的专属原子命令。
- Bitmaps:通过在字符串数据结构上使用位操作,有效节省内存,为开 发提供新的思路。
- HyperLogLog:一种基于概率的新算法,难以想象地节省内存空间。
- 发布订阅:基于发布订阅模 式的消息通信机制。
- GEO:Redis3.2提供了基于地理位置信息的功能。
慢查询分析
- 可以通过config set命令动态修改参数,并使配置持久化到配置文件中
config set slowlog-log-slower-than 20000
config set slowlog-max-len 1000
config rewrite
- 获取慢查询日志
# slowlog get [n]
下面操作返回当前Redis的慢查询,参数n可以指定条数:
127.0.0.1:6379> slowlog get
1) 1) (integer) 666
176
2) (integer) 1456786500
3) (integer) 11615
4) 1) "BGREWRITEAOF"
2) 1) (integer) 665
2) (integer) 1456718400
3) (integer) 12006
4) 1) "SETEX"
2) "video_info_200"
3) "300"
4) "2"
...
可以看到每个慢查询日志有4个属性组成,分别是慢查询日志的标识
id、发生时间戳、命令耗时、执行命令和参数
- 获取慢查询日志列表当前的长度
# slowlog len
例如,当前Redis中有45条慢查询:
127.0.0.1:6379> slowlog len
(integer) 45
- 慢查询日志重置
slowlog reset
实际是对列表做清理操作,例如:
127.0.0.1:6379> slowlog len
(integer) 45
127.0.0.1:6379> slowlog reset
OK
127.0.0.1:6379> slowlog len
(integer) 0
Redis shell
redis-cli 详解
- -r(repeat)选项代表将命令执行多次,例如下面操作将会执行三次ping 命令
redis-cli -r 3 ping
PONG
PONG
PONG
- -i(interval)选项代表每隔几秒执行一次命令,但是-i选项必须和-r选 项一起使用,下面的操作会 每隔1秒执行一次ping命令,一共执行5次
redis-cli -r 5 -i 1 ping
PONG
PONG
PONG
PONG
PONG
- -x选项代表从标准输入(stdin)读取数据作为redis-cli的最后一个参 数,例如下面的操作会将字符 串world作为set hello的值:
$ echo "world" | redis-cli -x set hello
OK
- -c(cluster)选项是连接Redis Cluster节点时需要使用的,-c选项可以防 止moved和ask异常
- 如果Redis配置了密码,可以用-a(auth)选项,有了这个选项就不需要 手动输入auth命令
redis-server 详解
- redis-server- -test-memory可以用来检测当前操作系统能否稳定地分配指定容量的内存给 Redis, 通过这种检测可以有效避免因为内存问题造成Redis崩溃,例如下面 操作检测当前操作系统能否提 供1G的内存给Redis
redis-server --test-memory 1024
- 整个内存检测的时间比较长。当输出passed this test时说明内存检测完 毕
redis-benchmark详解
- redis-benchmark可以为Redis做基准性能测试,它提供了很多选项帮助开 发和运维人员测试Redis 的相关性能
- c(clients)选项代表客户端的并发数量(默认是50)。
- n(num)选项代表客户端请求总量(默认是100000)。
- -q选项仅仅显示redis-benchmark的requests per second信息。
- 在一个空的Redis上执行了redis-benchmark会发现只有3个键, 如果想向Redis插入更多的键,可以 执行使用-r(random)选项,可以向 Redis插入更多随机的键。
Pipeline
- Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但 大部分命令是不支持批量 操作的,例如要执行n次hgetall命令,并没有 mhgetall命令存在,需要消耗n次RTT。
- Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进 行组装,通过一次RTT传输 给Redis,再将这组Redis命令的执行结果按顺序返回给客户端
echo -en '*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n*2\r\n$4\r\nincr\r\
n$7\r\ncounter\r\n' | redis-cli --pipe
Bitmaps
- 设置值
setbit key offset value
127.0.0.1:6379> setbit unique:users:2016-04-05 0 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 5 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 11 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 15 1
(integer) 0
127.0.0.1:6379> setbit unique:users:2016-04-05 19 1
(integer) 0
- 获取值
gitbit key offset
127.0.0.1:6379> getbit unique:users:2016-04-05 8
(integer) 0
- 获取Bitmaps指定范围值为1的个数
bitcount [start][end]
下面操作计算2016-04-05这天的独立访问用户数量:
127.0.0.1:6379> bitcount unique:users:2016-04-05
(integer) 5
[start]和[end]代表起始和结束字节数,下面操作计算用户id在第1个字节
到第3个字节之间的独立访问用户数,对应的用户id是11,15,19。
127.0.0.1:6379> bitcount unique:users:2016-04-05 1 3
(integer) 3
- Bitmaps间的运算
bitop是一个复合操作,它可以做多个Bitmaps的and(交集)、or(并
集)、not(非)、xor(异或)操作并将结果保存在destkey中。
bitop op destkey key[key....]
计算出2016-04-04和2016-04-03两天都访问过网站的用户数
127.0.0.1:6379> bitop and unique:users:and:2016-04-04_03 unique: users:2016-04-
03
unique:users:2016-04-03
(integer) 2
127.0.0.1:6379> bitcount unique:users:and:2016-04-04_03
(integer) 2
发布订阅
Redis提供了基于“发布/订阅”模式的消息机制,此种模式下,消息发布 者和订阅者不进行直接通信,发 布者客户端向指定的频道(channel)发布消 息,订阅该频道的每个客户端都可以收到该消息
发布消息
publish channel message
下面操作会向channel:sports频道发布一条消息“Tim won the
championship”,返回结果为订阅者个数,因为此时没有订阅,所以返回结果
为0:
127.0.0.1:6379> publish channel:sports "Tim won the championship"
(integer) 0
- 订阅消息
subscribe channel [channel ...]
订阅者可以订阅一个或多个频道,下面操作为当前客户端订阅了
channel:sports频道:
127.0.0.1:6379> subscribe channel:sports
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:sports"
3) (integer) 1
- 取消订阅
unsubscribe [channel [channel ...]]
客户端可以通过unsubscribe命令取消对指定频道的订阅,取消成功后,
不会再收到该频道的发布消息:
127.0.0.1:6379> unsubscribe channel:sports
1) "unsubscribe"
2) "channel:sports"
3) (integer) 0
- 按照模式订阅和取消订阅
psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]
除了subcribe和unsubscribe命令,Redis命令还支持glob风格的订阅命令
psubscribe和取消订阅命令punsubscribe,例如下面操作订阅以it开头的所有
频道:
127.0.0.1:6379> psubscribe it*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "it*"
- 查询订阅
(1)查看活跃的频道
pubsub channels [pattern]
所谓活跃的频道是指当前频道至少有一个订阅者,其中[pattern]是可以
指定具体的模式:
127.0.0.1:6379> pubsub channels
1) "channel:sports"
2) "channel:it"
3) "channel:travel"
127.0.0.1:6379> pubsub channels channel:*r*
1) "channel:sports"
2) "channel:travel"
(2)查看频道订阅数
pubsub numsub [channel ...]
当前channel:sports频道的订阅数为2:
127.0.0.1:6379> pubsub numsub channel:sports
1) "channel:sports"
2) (integer) 2
(3)查看模式订阅数
pubsub numpat
当前只有一个客户端通过模式来订阅:
127.0.0.1:6379> pubsub numpat
(integer) 1
GEO
Redis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信 息用来实现诸如附近位置、摇一 摇这类依赖于地理位置信息的功能,对于需 要实现这些功能的开发者来说是一大福音。
- 增加地理位置信息
geoadd key longitude latitude member [longitude latitude member ...]
127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing
(integer) 1
127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing
(integer) 0
27.0.0.1:6379> geoadd cities:locations 117.12 39.08 tianjin 114.29 38.02
shijiazhuang 118.01 39.38 tangshan 115.29 38.51 baoding
(integer) 4
- 获取地理位置信息
geopos key member [member ...]
下面操作会获取天津的经维度:
127.0.0.1:6379> geopos cities:locations tianjin
1) 1) "117.12000042200088501"
2) "39.0800000535766543"
- 获取两个地理位置的距离。
geodist key member1 member2 [unit]
其中unit代表返回结果的单位,包含以下四种:
m(meters)代表米。
km(kilometers)代表公里。
mi(miles)代表英里。
ft(feet)代表尺。
127.0.0.1:6379> geodist cities:locations tianjin beijing km
"89.2061"
- 获取指定位置范围内的地理信息位置集合
georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist]
[withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist]
[withhash] [COUNT count] [asc|desc] [store key] [storedist key]
withcoord:返回结果中包含经纬度。
withdist:返回结果中包含离中心节点位置的距离。
withhash:返回结果中包含geohash,有关geohash后面介绍。
COUNT count:指定返回结果的数量。
asc|desc:返回结果按照离中心节点的距离做升序或者降序。
store key:将返回结果的地理位置信息保存到指定键。
storedist key:将返回结果离中心节点的距离保存到指定键
127.0.0.1:6379> georadiusbymember cities:locations beijing 150 km
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"
- 删除地理位置信息
GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset(有序集合),所以
可以借用zrem命令实现对地理位置信息的删除
zrem key member
4.数据持久化
- redis支持RDB和AOF两种持久化机制,持久功能能有效的避免因进程退出造成的数据丢失问题;当下次重启时利用之前持久化的文件即可实现数据恢复。
- 因为redis是将数据运行在内存中的,如果出现服务挂掉或者服务器宕机都可以导致数据的全部丢失,为了解决这个问题redis提供了RDB/AOF解决方案
RDB持久化
简介
- RDB持久是把当前数据生成快照保存到硬盘的过程
- 触发RDB持久化过程为手动触发和自动触发
优点:
- RDB是一个非常紧凑的文件,他保存了redis在某个时间点上的数据集。RDB文件非常适用于进行备份和灾难恢复
- RDB在恢复大数据集时的速度比AOF的恢复速度要快
缺点:
- 影响性能:RDB方式数据没有办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作(内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑),频繁执行成本过高
- 版本不兼容:RDB文件使用特定二进制格式保存,Redis版本演化过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题
- 数据有丢失:在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改
触发方式
- 自动触发:主配置文件中的save字段
分别表示每900秒数据发生一次改变、每300秒数据发生10次改变、每60秒数据发生10000
次改变会自动触发rdb持久化机制
save 900 1
save 300 10
save 60 10000
手动触发
- save命令:阻塞当前redis服务器,直到RDB过程完成为止;对于内存比较大的实例会造成长时间堵塞,线上环境不建议使用
- bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短
- 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave
BGSAVE流程说明
- 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进 程,如RDB/AOF子进程,如果 存在bgsave命令直接返回。
- 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通 过info stats命令查看 latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒。
- 父进程fork完成后,bgsave命令返回“Background saving started”信息 并不再阻塞父进程,可以 继续响应其他命令。
- 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行 lastsave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项。
- 子进程发送信号给父进程表示完成,父进程更新统计信息
运维提示
- 当遇到坏盘或磁盘写满等情况时,可以通过config set dir{newDir}在线修改文件路径到可用的磁 盘路径,之后执行bgsave进行磁盘切换,同样适用于AOF持久化文件。
- Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远远小于内存大小,默认开 启,可以通过参数config set rdbcompression{yes|no}动态修改。
- 如果想要恢复相关数据,只需要将相关的RDB文件拷贝到相关的目录下面即可,redis启动时会自动 将rdb文件里的内容加载到内存中。
AOF持久化
简洁
- AOF持久化:以独立日志的方式记录每次写的命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的
- AOF的主要作用:解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
优点
- AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最 多也就丢失 1 秒的数据而已。
- AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的 片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。
- AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小 心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去 掉,然后再使用 AOF 来恢 复数据
缺点
- 对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。
- 虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但 在 Redis 的负载 较高时,RDB 比 AOF 具好更好的性能保证。
- RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从 理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一 些 BUG,这些 BUG在RDB没有存在
使用AOF
- 开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件名 通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致,通过dir配置指定。
appendonly yes # 将配置文件中appendonly字段设置为yes即可
- 所有的写入命令会追加到aof_buf(缓冲区)中。
- AOF缓冲区根据对应的策略向硬盘做同步操作。
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩 的目的。
- 当Redis服务器重启时,可以加载AOF文件进行数据恢复。
重写机制
- 随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入AOF重写机制压缩文件体积
重写后的AOF文件会变小,原因如下:
- 进程内已经超时的数据不再写入文件
- 旧的AOF文件含有无效命令,如del key1、hdel key2、srem keys、set a111、set a222等。 重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢 出,对于list、set、hash、zset等类型 操作,以64个元素为界拆分为多条
触发:
- 手动触发:直接调用bgrewriteaof命令
- 自动触发:根据auto-rewrite-min-size和auto-aof-rewrite-percentage参 数确定自动触发时 机。
执行AOF重写请求。
父进程执行fork创建子进程,开销等同于bgsave过程。
主进程fork操作完成后,继续响应其他命令。所有修改命令写入AOF缓冲区并根据 appendfsync策略同步到硬盘,保证原有AOF机制正确性。
由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。
子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每次批量写入硬盘数据量由配置 aof-rewrite-incremental-fsync控制,默认为 32MB,防止单次刷盘数据过多造成硬盘阻塞。
新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见info persistence 下的aof_*相关统计。
父进程把AOF重写缓冲区的数据写入到新的AOF文件。
使用新AOF文件替换老文件,完成AOF重写。
重启加载持久化文件过程
- AOF持久化开启且存在AOF文件时,优先加载AOF文件
- AOF关闭或者AOF文件不存在时,加载RDB文件
- 加载AOF/RDB文件成功后,Redis启动成功。
- AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。
5.架构-主从复制
- 在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到 其他机器,满足故障恢复和 负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。 复制功能是高可用Redis 的基础,后面章节的哨兵和集群都是在复制的基础上实现高可用的。
优点
- 满足故障和负载均衡等要求
缺点
- 若主节点出现问题则不能提高服务,需要人工修改配置将从变主,无法实现高可用
- 主从复制主节点的写能力有限
- 单机节点的存储能力有限
数据同步方式
全量同步
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。 步骤如下:
- 从服务器连接主服务器,发送SYNC命令; 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
增量同步
- Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
- 增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接 收并执行收到的写命令
同步策略
- 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。
- redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
实验部署
- 创建相关目录
mkdir /redis/638{0,1,2}/{data,conf,log} -pv
- 一台主服务器的配置文件
cat /redis/6380/conf/redis.conf
bind 127.0.0.1
port 6380
daemonize yes
pidfile /redis/redis_6380.pid
loglevel notice
logfile /redis/6380/log/redis_6380.log
dir /redis/6380/data/
- 从服务器配置
#########
cat /redis/6381/conf/redis.conf
bind 127.0.0.1
port 6381
daemonize yes
pidfile /redis/redis_6381.pid
loglevel notice
logfile /redis/6381/log/redis_6380.log
dir /redis/6381/data/
#########
cat /redis/6381/conf/redis.conf
bind 127.0.0.1
port 6382
daemonize yes
pidfile /redis/redis_6382.pid
loglevel notice
logfile /redis/6382/log/redis_6380.log
dir /redis/6382/data/
- 启动则完成主从复制架构的部署
redis-server /redis/6380/conf/redis6380.conf
redis-server /redis/6381/conf/redis6381.conf
redis-server /redis/6382/conf/redis6382.conf
- 验证测试
127.0.0.1:6380> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=1334,lag=1
slave1:ip=127.0.0.1,port=6382,state=online,offset=1334,lag=1
master_repl_offset:1334
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:1333
127.0.0.1:6380> set name yingges
OK
127.0.0.1:6380> exit
[root@node1 ~]# redis-cli -p 6381
127.0.0.1:6381> get name
"yingges"
127.0.0.1:6381> set name2 redis
(error) READONLY You can't write against a read only slave.
##建议可以使用三台机器部署实验,ip地址分别改为新的地址,因为主从复制在一台机器上没有现实意义
6.架构-哨兵
- Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点, 同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
工作原理
- 每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着, 如果发现对方在 指定时间( down-after-milliseconds项 )内未回应,则暂时认为对方已挂(主观宕 机:sdown),若“哨兵群”中的多数 sentinel,都报告某一master没响应,系统才认为该master”彻底死亡” (即:客观上的真正down机:odown),通过一定的 vote算法,从剩下的slave节点中,选一台提升为 master,然后自动修改相关配置。
实验部署
- 创建相关目录
mkdir /redis/263{78,79,80}/{data,conf,log} -pv
- 部署哨兵1:监听26378端口
vi /redis/26378/conf/redis.conf
port 26378
daemonize yes
dir "/redis/26378/data/"
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
logfile "/redis/26378/log/sentinel.log.26378"
- 部署哨兵2:监听26379端口
vi /redis/26379/conf/redis.conf
port 26379
daemonize yes
dir "/redis/26379/data/"
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
logfile "/redis/26378/log/sentinel.log.26379"
- 部署哨兵3:监听26380端口
vi /redis/26379/conf/redis.conf
port 26380
daemonize yes
dir "/redis/26380/data/"
sentinel monitor mymaster 127.0.0.1 6380 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
logfile "/redis/26378/log/sentinel.log.26380"
- 启动哨兵服务
redis-server /redis/26378/conf/redis.conf --sentinel
redis-server /redis/26379/conf/redis.conf --sentinel
redis-server /redis/26380/conf/redis.conf --sentinel
通常情况下我们尽量启动3个哨兵(奇数)及以上监听一个主节点。
验证测试
#关闭master上redis服务
ps -ef|grep 6380
kill -9
#6382端口从服务器自动变成master
127.0.0.1:6382> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=131255,lag=0
slave1:ip=127.0.0.1,port=6380,state=online,offset=131388,lag=0
master_repl_offset:131388
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:131387
7.架构-集群
- redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
优点:
- 将Redis的写操作分摊到了多个节点上,提高写的并发能力;扩容简单
缺点:
- 每个node承担着互相监听,高并发数据写入,高并发数据读出,工作任务繁重
工作原理
- 对象保存到Redis之前先经过CRC16哈希到一个指定的Node上。
- 每个Node被平均分配了一个Slot段,对应着0-16384,Slot不能重复也不能缺失,否则会导致对象 重复存储或无法 存储。
- Node之间也互相监听,一旦有Node退出或者加入,会按照Slot为单位做数据的迁移。例如Node1 如果掉线了,0- 3276这些Slot将会平均分摊到Node2和Node3上,由于Node2和Node3本身维护的 Slot还会在自己身上不会被重新分配,所以迁移过程中不会影响到3277-16384Slot段的使用。
实验部署
- 环境准备
#!/bin/bash
yum install gcc-c++ -y
wget http://download.redis.io/releases/redis-5.0.4.tar.gz
tar xzf redis-5.0.4.tar.gz
cd redis-5.0.4
make install PREFIX=/usr/local/redis
PATH=$PATH:/usr/local/redis/bin
- 部署
[root@node2 ~]# mkdir /usr/local/redis-cluster
[root@node2 ~]# cd /usr/local/redis-cluster/
[root@node2 redis-cluster]# bash -x redis-cluster.sh
#!/bin/bash
mkdir /usr/local/redis-cluster/redis{7000..7005} -pv
touch /usr/local/redis-cluster/redis{7000..7005}/redis.conf
#!/bin/bash
for i in {7000..7005};
do
cat << EOF > /usr/local/redis-cluster/redis$i/redis.conf
daemonize yes
port $i
cluster-enabled yes
cluster-config-file /usr/local/redis-cluster/redis$i/nodes-$i.conf
cluster-node-timeout 5000
appendonly yes
EOF
redis-server /usr/local/redis-cluster/redis$i/redis.conf
done
#检查各进程是否ok
[root@node2 local]# ps -ef | grep redis
root 5761 1 0 03:46 ? 00:00:00 redis-server *:7000 [cluster]
root 5767 1 0 03:46 ? 00:00:00 redis-server *:7001 [cluster]
root 5773 1 0 03:46 ? 00:00:00 redis-server *:7002 [cluster]
root 5779 1 0 03:46 ? 00:00:00 redis-server *:7003 [cluster]
root 5785 1 0 03:46 ? 00:00:00 redis-server *:7004 [cluster]
root 5791 1 0 03:46 ? 00:00:00 redis-server *:7005 [cluster]
[root@node2 local]# redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
> 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
> --cluster-replicas 1
#输入yes,即按照此方式分配master和slave
M: 511ba9acbff21c59de7654342dc3847a9c9309d7 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: fb3ee7237c0a1e164469f312b8fd65379139e6f0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: 970f5bbc15256edbdd4587dda6dd4050dc6c651a 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 37bef4ed48428e9c530b5c963c3afc3975a7049b 127.0.0.1:7003
replicates fb3ee7237c0a1e164469f312b8fd65379139e6f0
S: fd4c06655852c01b5c850d438cf4eca71a101a70 127.0.0.1:7004
replicates 970f5bbc15256edbdd4587dda6dd4050dc6c651a
S: 86ef91e58e5736bcda38582d824242bbf74d2d25 127.0.0.1:7005
replicates 511ba9acbff21c59de7654342dc3847a9c9309d7
- 验证测试
[root@node2 local]# redis-cli -c -p 7000
#查询集群信息
127.0.0.1:7000> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:353
cluster_stats_messages_pong_sent:357
cluster_stats_messages_sent:710
cluster_stats_messages_ping_received:352
cluster_stats_messages_pong_received:353
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:710
#查询集群各节点
127.0.0.1:7000> cluster nodes
86ef91e58e5736bcda38582d824242bbf74d2d25 127.0.0.1:7005@17005 slave
511ba9acbff21c59de7654342dc3847a9c9309d7 0 1554278064038 6 connected
fd4c06655852c01b5c850d438cf4eca71a101a70 127.0.0.1:7004@17004 slave
970f5bbc15256edbdd4587dda6dd4050dc6c651a 0 1554278062005 5 connected
970f5bbc15256edbdd4587dda6dd4050dc6c651a 127.0.0.1:7002@17002 master - 0
1554278063031 3
connected 10923-16383
37bef4ed48428e9c530b5c963c3afc3975a7049b 127.0.0.1:7003@17003 slave
fb3ee7237c0a1e164469f312b8fd65379139e6f0 0 1554278062000 4 connected
fb3ee7237c0a1e164469f312b8fd65379139e6f0 127.0.0.1:7001@17001 master - 0
1554278062529 2
connected 5461-10922
511ba9acbff21c59de7654342dc3847a9c9309d7 127.0.0.1:7000@17000 myself,master - 0
1554278063000 1 connected 0-5460
[root@node2 local]# redis-cli -c -p 7000
127.0.0.1:7000> set name eagles
-> Redirected to slot [5798] located at 127.0.0.1:7001
OK # 数据分配特性
