Reids介绍
Redis是由C语言开发的,开源的键值对(key-value)数据库。支持存储的value类型包括string(字符串)、list(链表)、set(集合)、zset(sorted set有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove、交集并集和差集等操作,这些操作都是原子性的——不可分割。
与memcached一样,为了保证效率,数据缓存在内存中。区别是Redis会周期性地把数据写入磁盘或把操作追加到记录文件中,以实现master-slave(主从)同步。
特点
- 速度快,虽然Redis是单线程架构,但是数据运行在内存中,所以运行速度非常快。
- 支持丰富的数据类型,五种基本数据类型:string、list、set、zset、hash
- 支持事务
- 丰富的功能特性
Redis单线程架构
- redis是单线程架构,命令从客户端发到服务端不是立即执行的,所有命令都是进入一个队列中,然后被执行。另外redis采用了I/O多路复用技术解决I/O问题。
- 为什么单线程架构的Redis速度可以很快。
- 第一,纯内存访问,Redis将所有数据放在内存中,内存的响应大约是100纳秒,这是redis达到每秒万级别的访问的重要基础。
- 第二,非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,加上redis自身的实现处理模型将epoll的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。
- 第三,单线程避免了线程切换和竞态产生的消耗
- 客户端和服务端请求过程
- 所有命令在一个队列里等待被执行
安装部署
软件安装
[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 # 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@server1 ~]# cat /etc/redis.conf | grep -Ev '^#|^$'===========================基础配置=======================bind 127.0.0.1 # 绑定IP地址protected-mode yesport 6379 # 绑定端口tcp-backlog 511timeout 0 # 客户端连接超时时间tcp-keepalive 300 # 检测客户端是否健康的周期时间daemonize no # 是否以守护进程的形式启动supervised nopidfile /var/run/redis_6379.pid # pid文件loglevel notice # 日志等级logfile /var/log/redis/redis.log # 日志文件databases 16 # 数据库数目==========================RDB============================save 900 1 # 900秒内至少一个key被修改则发器快照save 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 # 配置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给客户端loadlua-time-limit 5000 # 一个lua脚本执行的最大时间slowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yes
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>
Redis基础命令
命令手册: http://redisdoc.com/index.html
全局命令
KEYS *:查看所有键,该命令在生产环境中要慎用,会一次性读取所有键,可能会导致阻塞。
127.0.0.1:6379> KEYS *1) "k3"2) "k1"3) "k4"4) "k2"
dbsize:查看当前数据库中所有键的数量
127.0.0.1:6379> DBSIZE(integer) 4
exists:检查某个键是否存在,存在返回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)
type key:查看键的类型,数据结构类型有五大基本数据类型(string、list、set、zset、hash)
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 keyincr命令用于对值做自增操作,返回结果分为三种情况:·值不是整数,返回错误。·值是整数,返回自增后的结果。·键不存在,按照值为0自增,返回结果为1。例如对一个不存在的键执行incr操作后,返回结果是1:127.0.0.1:6379> exists key(integer) 0127.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 worldOK127.0.0.1:6379> incr hello(error) ERR value is not an integer or out of range
不常用命令(扩展)
1)追加值append key valueappend可以向字符串尾部追加值,例如:127.0.0.1:6379> get key"redis"127.0.0.1:6379> append key world(integer) 10127.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个字节:127.0.0.1:6379> set hello "世界"OK127.0.0.1:6379> strlen hello(integer) 6(3)设置并返回原值getset key valuegetset和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 pestOK127.0.0.1:6379> setrange redis 0 b(integer) 4127.0.0.1:6379> get redis"best"(5)获取部分字符串getrange key start endstart和end分别是开始和结束的偏移量,偏移量从0开始计算,例如下面操作获取了值best的前两个字符。127.0.0.1:6379> getrange redis 0 1"be"
列表命令
列表(list)类型用来存储多个有序的字符串,是一种线性结构,可以充当栈和队列的角色
- 添加操作 ```bash (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”
- 查找```bash(1)获取指定范围内的元素列表lrange key start endlrange操作会获取列表指定索引范围所有的元素。索引下标有两个特点:第一,索引下标从左到右分别是0到N-1,但是从右到左分别是-1到-N。第二,lrange中的end选项包含了自身,这个和很多编程语言不包含end不太相同,例如想获取列表的第2到第4个元素,可以执行如下操作:127.0.0.1:6379> lrange listkey 1 31) "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
- 删除 ```bash (1)从列表左侧弹出元素 lpop key 如下操作将列表最左侧的元素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”
- 修改```bash修改指定索引下标的元素:lset key index newValue下面操作会将列表listkey中的第3个元素设置为python:127.0.0.1:6379> lset listkey 2 pythonOK127.0.0.1:6379> lrange listkey 0 -11) "java"2) "b"3) "python"
哈希命令
几乎所有编程语言都提供了hash类型,他们的叫法可能是哈希、字典、关联数组。常常会使用hash数据类型存储关系型数据库中的表。
设置值
hset key field value127.0.0.1:6379> hset user:1 name tom(integer) 1
获取值
hget key field127.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) 1127.0.0.1:6379> hdel user:1 age(integer) 0
计算field个数
hlen key127.0.0.1:6379> hset user:1 name tom(integer) 1127.0.0.1:6379> hset user:1 age 23(integer) 1127.0.0.1:6379> hset user:1 city tianjin(integer) 1127.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和多个field127.0.0.1:6379> hmset user:1 name mike age 12 city tianjinOK127.0.0.1:6379> hmget user:1 name city1) "mike"2) "tianjin"
获取所有field
hkeys keyhkeys命令应该叫hfields更为恰当,它返回指定哈希键所有的field127.0.0.1:6379> hkeys user:11) "name"2) "age"3) "city"
获取所有value
hvals key下面操作获取user:1全部value:127.0.0.1:6379> hvals user:11) "mike"2) "12"3) "tianjin"
获取所有的field-value
hgetall key下面操作获取user:1所有的field-value:127.0.0.1:6379> hgetall user:11) "name"2) "mike"3) "age"4) "12"5) "city"6) "tianjin"
集合命令
集合(set)类型也是用来保存多个的字符串元素,集合中不允许有重复出现的元素,集合的元素是没有顺序的不能够通过下标来获得值
添加元素
sadd key element [element ...]返回结果为添加成功的元素个数127.0.0.1:6379> exists myset(integer) 0127.0.0.1:6379> sadd myset a b c(integer) 3127.0.0.1:6379> sadd myset a b(integer) 0
删除元素
srem key element [element ...]返回结果为成功删除元素个数127.0.0.1:6379> srem myset a b(integer) 2127.0.0.1:6379> srem myset hello(integer) 0
计算元素个数
scard keyscard的时间复杂度为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]是可选参数,如果不写默认为1127.0.0.1:6379> srandmember myset 21) "a"2) "c"127.0.0.1:6379> srandmember myset"d"
集合随机弹出元素
spop keyspop操作可以从集合中随机弹出一个元素,例如下面代码是一次spop后,集合元素变为"d b a":127.0.0.1:6379> spop myset"c"127.0.0.1:6379> smembers myset1) "d"2) "b"3) "a"
获取所有元素
smembers key下面代码获取集合myset所有元素,并且返回结果是无序的:127.0.0.1:6379> smembers myset1) "d"2) "b"3) "a"
交集运算
127.0.0.1:6379> sadd user:1:follow it music his sports(integer) 4127.0.0.1:6379> sadd user:2:follow it news ent sports(integer) 4sinter key [key ...]例如下面代码是求user:1:follow和user:2:follow两个集合的交集,返回结果是sports、it:127.0.0.1:6379> sinter user:1:follow user:2:follow1) "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:follow1) "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:follow1) "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) 2127.0.0.1:6379> type user:1_2:interset127.0.0.1:6379> smembers user:1_2:inter1) "it"2) "sports"
有序集合命令
有序集合保留了集合不能有重复成员的特性,不同的是有序集合中的元素是可以排序的。但是它和列表使用索引下标作为排序依据不同的是它给每个元素设置一个分数(score)作为排序的依据
添加成员 ```bash 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必须存在,才可以设置成功,用于更新。 ch:返回此次操作后,有序集合元素和分数发生变化的个数 incr:对score做增加,相当于后面介绍的zincrby。
- 计算成员个数```bashzcard key例如下面操作返回有序集合user:ranking的成员数为5,和集合类型的scard命令一样,zcard的时间复杂度为O(1)。127.0.0.1:6379> zcard user:ranking(integer) 5
计算某个成员的分数
zscore key membertom的分数为251,如果成员不存在则返回nil:127.0.0.1:6379> zscore user:ranking tom"251"127.0.0.1:6379> zscore user:ranking test(nil)
计算某个成员的排名
zrank key memberzrevrank key memberzrank是从分数从低到高返回排名,zrevrank反之。例如下面操作中,tom在zrank和zrevrank分别排名第5和第0(排名从0开始计算)。127.0.0.1:6379> zrank user:ranking tom(integer) 5127.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 withscores1) "kris"2) "1"3) "frank"4) "200"5) "tim"6) "220"127.0.0.1:6379> zrevrange user:ranking 0 2 withscores1) "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 withscores1) "frank"2) "200"3) "tim"4) "220"127.0.0.1:6379> zrevrangebyscore user:ranking 221 200 withscores1) "tim"2) "220"3) "frank"4) "200"同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大:127.0.0.1:6379> zrangebyscore user:ranking (200 +inf withscores1) "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 martin251 tom(integer) 6127.0.0.1:6379> zadd user:ranking:2 8 james 77 mike 625 martin 888 tom(integer) 4zinterstore 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) 3127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores1) "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) 3127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores1) "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) 7127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores1) "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"
命令复杂度(了解)
Redis丰富特性
redis除了提供五大基本数据类型,还提供了丰富的功能强大的附加功能。
- 慢查询分析:找到有问题的命令进行优化
- Redis Shell:功能强大的Shell,具有实用功能
- Pipeline:通过Pipeline(管道或流水线)机制有效提高客户端性能
- 事务与Lua:只做自己专属的原子命令
- Bitmaps:在字符串数据结构上使用位操作,有效节省内存,为开发提供新思路。
- 发布订阅:基于发布订阅模式的消息通信机制
-
慢查询分析
config set
Redis Config Set 命令可以动态地调整 Redis 服务器的配置(configuration)而无须重启。你可以使用它修改配置参数,或者改变 Redis 的持久化(Persistence)方式。
# 格式redis 127.0.0.1:6379> CONFIG Set parameter valueconfig set slowlog-log-slower-than 20000 # 对执行时间大于多少微秒的查询进行记录config set slowlog-max-len 1000 # 它决定 slow log 最多能保存多少条日志, slow log 本身是一个 FIFO 队列,当队列大小超过 slowlog-max-len 时,最旧的一条日志将被删除,而最新的一条日志加入到 slow log ,以此类推config rewrite #将当前配置改写到配置文件中。
获取慢查询日志
slow get [n]下面操作返回当前Redis的慢查询,参数n可以指定条数:127.0.0.1:6379> slowlog get1) 1) (integer) 666 # 标识176 # ID2) (integer) 1456786500 # 时间戳3) (integer) 11615 # 命令耗时4) 1) "BGREWRITEAOF" # 执行命令2) 1) (integer) 6652) (integer) 14567184003) (integer) 120064) 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) 45127.0.0.1:6379> slowlog resetOK127.0.0.1:6379> slowlog len(integer) 0
Redis Shell
redis-cli详解
-r(repeat)选项代表将命令执行多次,例如下面操作将会执行三次ping 命令
[root@server1 ~]# redis-cli -r 3 pingPONGPONGPONG
-i(interval)选项代表每隔几秒执行一次命令,但是-i选项必须和-r选 项一起使用,下面的操作会每隔1秒执行一次ping命令,一共执行5次
[root@server1 ~]# redis-cli -r 5 -i 1 pingPONGPONGPONGPONGPONG
-x选项代表从标准输入(stdin)读取数据作为redis-cli的最后一个参数,例如下面的操作会将字符串world作为set hello的值:
[root@server1 ~]# echo 'world' | redis-cli -x set helloOK
-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(Round-Trip Time:往返时延)。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令,并没有 mhgetall命令存在,需要消耗n次RTT。
- Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端
![S28UP5JJ9B1NQ~3BEI`3)K.png
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
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身,value对应0或1,我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。
设置值
setb+it key offset value127.0.0.1:6379> setbit unique:users:2016-04-05 0 1(integer) 0127.0.0.1:6379> setbit unique:users:2016-04-05 5 1(integer) 0127.0.0.1:6379> setbit unique:users:2016-04-05 11 1(integer) 0127.0.0.1:6379> setbit unique:users:2016-04-05 15 1(integer) 0127.0.0.1:6379> setbit unique:users:2016-04-05 19 1(integer) 0
![S2~2FFVB}EMNN4}J2~H27E.png
获取值
gitbit key offset127.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个字节之间(第8位到第24位之间)的独立访问用户数,对应的用户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 option 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-04(integer) 2127.0.0.1:6379> bitcount unique:users:and:2016-04-04_03(integer) 2
发布订阅
Redis提供了基于“发布/订阅”模式的消息机制,此种模式下,消息发布者和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息。

发布消息
publish channel message下面操作会向channel:sports频道发布一条消息“Tim won thechampionship”,返回结果为订阅者个数,因为此时没有订阅,所以返回结果为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:sportsReading 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:sports1) "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*"
查询订阅 ```bash (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
<a name="cvGnv"></a>## GEORedis3.2版本提供了GEO(地理信息定位)功能,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需要实现这些功能的开发者来说是一大福音。- 增加地理位置信息 (返回添加地理位置的个数)```bashgeoadd key longitude latitude member [longitude latitude member ...]127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing(integer) 1127.0.0.1:6379> geoadd cities:locations 116.28 39.55 beijing(integer) 0127.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 tianjin1) 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 km1) "beijing"2) "tianjin"3) "tangshan"4) "baoding"
删除地理位置
GEO没有提供删除成员的命令,但是因为GEO的底层实现是zset,所以可以借用zrem命令实现对地理位置信息的删除zrem key member
数据持久化
Redis支持RDB和AOF两种持久化机制,持久化功能能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前之间持久化的文件即可实现数据恢复。简单点说,redis是将数据运行在内存中的,如果出现服务挂掉或者服务器宕机都可以导致数据全部丢失,为了解决这个问题redis提供了两种解决方案,分别是rdb、aof
RDB持久化
简介
- RDB(Redis DataBase)持久是把当前数据生成快照保存到硬盘的过程
触发RDB持久化过程为手动触发和自动触发
优点
- RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复。
- RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
缺点
自动触发:主配置文件中的save字段
分别表示每900秒数据发生一次改变、每300秒数据发生10次改变、每60秒数据发生10000次改变会自动触发rdb持久化机制save 900 1save 300 10save 60 10000
手动触发
- save命令:阻塞当前Redis服务器,直到RDB过程完成为止。对于内存较大的实例会造成长时间阻塞,不建议线上环境使用。
- bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。
- 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave。
bgsave流程说明

- 执行bgsave命令,父进程检查是否有正在执行的子进程,如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(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。
AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。
优点
- AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。
- AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。
- AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢 复数据。
缺点
开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件名 通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致,通过dir配置指定。
appendonly yes # 将配置文件中appendonly字段设置为yes即可
工作流程

- 所有写入命令会加入到aof_buff缓冲区中
- 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-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
![$89EV0{K_]N)T`%_L3(U$)D.png](/uploads/projects/u427629@ge071f/aafbbd2d94e38337e6e7a81c84a44b77.png)
- 执行AOF重写请求
父进程执行fork创建子进程,开销等同于bgsave过程。
3.1 主进程fork操作完成后继续响应其他命令。所有修改命令继续写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制正确性。
3.2 由于fork操作运用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然响应命令,Redis使用“AOF重写缓冲区”保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。子进程根据内存快照,按照命令合并规则写入到新的AOF文件。每次批量写入硬盘数据量由配置aof-rewrite-incremental-fsync控制,默认为32MB,防止单次刷盘数据过多造成硬盘阻塞。
5.1 新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见info persistence下的aof_*相关统计。
5.2 父进程把AOF重写缓冲区的数据写入到新的AOF文件。
5.3 使用新AOF文件替换老文件,完成AOF重写。
重启加载持久文件

- AOF持久化开启且存在AOF文件时,优先加载AOF文件。
- AOF关闭或者AOF文件不存在时,加载RDB文件
- 加载AOF/RDB文件后,Redis启动成功
- AOF/RDB文件存在错误时,redis启动失败并打印错误信息。
架构——主从复制
简介
在分布式系统中为了解决单点问题,通常会把数据复制多个副本部署到其他机器,满足故障恢复和负载均衡等需求。Redis也是如此,它为我们提供了复制功能,实现了相同数据的多个Redis副本。复制功能是高可用Redis的基础,后面章节的哨兵和集群都是在复制的基础上实现高可用的。
- 优点:满足故障和负载均衡等需求
缺点
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。步骤如下:
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
同步策略
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
实验部署
该实验就在一台主机上配置。
一台主服务器的配置文件
[root@server1 ~]# mkdir /redis/6380/{data,conf,log} -pv[root@server1 ~]# cd /redis/6380/conf[root@server1 conf]# vim redis.confbind 127.0.0.1port 6380daemonize yespidfile /redis/redis_6380.pidloglevel noticelogfile /redis/6380/log/redis_6380.logdir /redis/6380/data/
两台从服务器的配置文件
[root@server1 ~]# mkdir /redis/638{1,2}/{data,conf,log} -pv[root@server1 ~]# vim /redis/6381/conf/redis.confbind 127.0.0.1port 6381daemonize yespidfile /redis/redis_6381.pidloglevel noticelogfile /redis/6381/log/redis_6381.logdir /redis/6381/data/slaveof 127.0.0.1 6380[root@server1 ~]# vim /redis/6382/conf/redis.confbind 127.0.0.1port 6382daemonize yespidfile /redis/redis_6382.pidloglevel noticelogfile /redis/6382/log/redis_6382.logdir /redis/6382/data/slaveof 127.0.0.1 6380
启动则完成主从复制架构的部署。
[root@server1 conf]# redis-server /redis/6380/conf/redis.conf[root@server1 conf]# redis-server /redis/6381/conf/redis.conf[root@server1 conf]# redis-server /redis/6382/conf/redis.conf
验证测试 ```bash [root@server1 ~]# redis-cli -h 127.0.0.1 -p 6380 127.0.0.1:6380> info Replication
Replication
role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6381,state=online,offset=352,lag=1 slave1:ip=127.0.0.1,port=6382,state=online,offset=352,lag=1 master_repl_offset:352 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:351 127.0.0.1:6380> set time 22:16 OK
[root@server1 ~]# redis-cli -h 127.0.0.1 -p 6381 127.0.0.1:6381> get time “22:16” 127.0.0.1:6381>
- 建议可以使用三台机器部署实验,ip地址分别改为新的地址,因为主从复制在一台机器上没有现实意义<a name="ZyolG"></a># 架构——哨兵<a name="fDd0A"></a>## 简介Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要**人工**将从节点晋升为主节点,同时还要通知应用方更新主节点地址,对于很多应用场景这种故障处理的方式是无法接受的。<br />**Redis较难支持在线扩容**,在集群容量达到上限时在线扩容会变得很复杂。<a name="qHBij"></a>## 工作原理每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否“活”着,如果发现对方在指定时间( down-after-milliseconds项 )内未回应,则暂时认为对方已挂(主观宕机:sdown);<br />若“哨兵群”中的多数 sentinel,都报告某一master没响应,系统才认为该master“彻底死亡”(即:客观上的真正down机:odown),通过一定的 vote 算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。<br /><a name="PPqY6"></a>## 实验部署- 哨兵配置文件```bash[root@server1 ~]# mkdir /redis/263{78,79,80}/{data,conf,log} -pv[root@server1 ~]# cd /redis[root@server1 redis]# vim 26378/conf/sentinel_26378.confport 26378 # 监听端口26378daemonize yes # 以守护线程的方式启动,可以在后台运行dir "/redis/26378/data"sentinel monitor mymaster 127.0.0.1 6380 2 # 监听的master的名字、ip、端口号,2表示集群中两个# 哨兵主观认为master挂掉,才会真正客观认为master挂掉sentinel down-after-milliseconds mymaster 60000 # 60000毫秒没有回应哨兵则认为master失效sentinel failover-timeout mymaster 180000 # 180秒没有完成failover,则认为主备切换/故障转移失败sentinel parallel-syncs mymaster 1 # 在进行failover时最多可以有多少个slave对新的master同步操作logfile "/redis/26378/log/sentinel.26378.log"[root@server1 redis]# vim 26379/conf/sentinel_26379.confport 26379daemonize yesdir "/redis/26379/data"sentinel monitor mymaster 127.0.0.1 6380 2sentinel down-after-milliseconds mymaster 60000sentinel failover-timeout mymaster 180000sentinel parallel-syncs mymaster 1logfile "/redis/26379/log/sentinel.26379.log"[root@server1 redis]# vim 26380/conf/sentinel_26380.confport 26380daemonize yesdir "/redis/26380/data"sentinel monitor mymaster 127.0.0.1 6380 2sentinel down-after-milliseconds mymaster 60000sentinel failover-timeout mymaster 180000sentinel parallel-syncs mymaster 1logfile "/redis/26380/log/sentinel.26380.log"
启动哨兵服务
[root@server1 redis]# redis-sentinel /redis/26378/conf/sentinel_26378.conf[root@server1 redis]# redis-sentinel /redis/26379/conf/sentinel_26379.conf[root@server1 redis]# redis-sentinel /redis/26380/conf/sentinel_26380.conf
ps:通常情况下我们尽量启动3个哨兵(奇数)及以上监听一个主节点。
关闭服务:ps -ef | grep redis , kill -9 pid验证测试 ```bash
启动主从服务器
[root@server1 redis]# redis-server /redis/6380/conf/redis.conf [root@server1 redis]# redis-server /redis/6381/conf/redis.conf [root@server1 redis]# redis-server /redis/6382/conf/redis.conf
模拟主节点挂掉
[root@server1 ~]# redis-cli -h 127.0.0.1 -p 6380 127.0.0.1:6380> shutdown not connected>
或者使用kill进程的方式模拟
ps -ef |grep 6380 kill -9 pid
登录从节点查看,发现主节点变成6381了
[root@server1 ~]# redis-cli -h 127.0.0.1 -p 6382 127.0.0.1:6382> info replication
Replication
role:slave master_host:127.0.0.1 master_port:6381 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:54281 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
重新登录6380,发现6380变成了从节点
[root@server1 ~]# redis-server /redis/6380/conf/redis.conf [root@server1 ~]# redis-cli -h 127.0.0.1 -p 6380 127.0.0.1:6380> info replication
Replication
role:slave master_host:127.0.0.1 master_port:6381 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:91677 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
查看日志文件```bash[root@server1 redis]# vim /redis/26378/log/sentinel.26378.log# 主观认为master挂掉了7448:X 22 Mar 16:32:31.687 # +sdown master mymaster 127.0.0.1 63807448:X 22 Mar 16:32:31.758 # +new-epoch 1# 投票选举7448:X 22 Mar 16:32:31.761 # +vote-for-leader abd3d9e1dd1b1843ec11223c7efcaa51699150f5 1# 客观认为master挂了7448:X 22 Mar 16:32:31.764 # +odown master mymaster 127.0.0.1 6380 #quorum 2/27448:X 22 Mar 16:32:31.765 # Next failover delay: I will not start a failover before Mon Mar 22 16:38:31 2021# 重新配置哨兵配置文件7448:X 22 Mar 16:32:32.965 # +config-update-from sentinel abd3d9e1dd1b1843ec11223c7efcaa51699150f5 127.0.0.1 26379 @ mymaster 127.0.0.1 6380# 主备切换7448:X 22 Mar 16:32:32.965 # +switch-master mymaster 127.0.0.1 6380 127.0.0.1 63817448:X 22 Mar 16:32:32.965 * +slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6381
架构——集群
简介
redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
- 优点:将Redis的写操作分摊到了不同节点上,提高写的并发能力,扩容简单。
缺点:每个节点都要相互监听、高并发数据写入和读出,工作任务繁重。
工作原理
对象保存到Redis之前先经过CRC16哈希到一个指定的Node上。
- 每个Node被平均分配了一个Slot段,对应着0-16384,Slot不能重复也不能缺失,否则会导致对象重复存储或无法存储。
- Node之间也互相监听,一旦有Node退出或者加入,会按照Slot为单位做数据的迁移。例如Node1如果掉线了,0- 3276这些Slot将会平均分摊到Node2、3、4、5上,由于Node2、3、4、5本身维护的Slot还会在自己身上不会被重新分配,所以迁移过程中不会影响到3277-16384Slot段的使用。
实验部署
官方文档:https://redis.io/topics/cluster-tutorial
安装5.0版本的redis(旧版本需要用redis-trib.rb工具创建集群)
[root@server1 ~]# yum install gcc-c++ -y[root@server1 ~]# wget http://download.redis.io/releases/redis-5.0.4.tar.gz[root@server1 ~]# tar xzvf redis-5.0.4.tar.gz[root@server1 redis-5.0.4]# make install PREFIX=/usr/local/redis[root@server1 redis-5.0.4]# PATH=$PATH:/usr/local/redis/bin
创建Redis集群 ```bash [root@server1 redis]# mkdir /redis-cluster/{7000..7005} -pv
[root@server1 redis]# vim /redis-cluster/7000/redis.conf port 7000 daemonize yes cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
改一下端口号和nodes文件,其他配置文件相同
[root@server1 redis]# vim /redis-cluster/7001/redis.conf port 7001 daemonize yes cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 5000 appendonly yes [root@server1 redis]# vim /redis-cluster/7002/redis.conf [root@server1 redis]# vim /redis-cluster/7003/redis.conf [root@server1 redis]# vim /redis-cluster/7004/redis.conf [root@server1 redis]# vim /redis-cluster/7005/redis.conf
[root@server1 ~]# for i in {7000..7005};do redis-server /redis-cluster/$i/redis.conf;done [root@server1 redis]# ps -ef | grep redis root 7618 1 0 17:49 ? 00:00:01 redis-server :7000 [cluster] root 7651 1 0 17:54 ? 00:00:00 redis-server :7001 [cluster] root 7669 1 0 17:56 ? 00:00:00 redis-server :7003 [cluster] root 7673 1 0 17:56 ? 00:00:00 redis-server :7004 [cluster] root 7677 1 0 17:56 ? 00:00:00 redis-server *:7005 [cluster] root 7681 7389 0 17:56 pts/0 00:00:00 grep —color=auto redis
[root@server1 ~]# 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
选项—cluster-replicas 1意味着我们要为每个创建的主控节点创建一个从属节点。
输入yes,即按照此方式分配master和slave
M: 6eeea311f17374abebde4dff97fa957db8b3ddd3 127.0.0.1:7000 slots:[0-5460] (5461 slots) master M: a7856400b2e82b8d18fe64d908a4f9cd46dbad41 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master M: 0832bbcd7d6843f7da8c38a8683293b58e12fbc2 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master S: 5b6794e2944d0d45e316f0bf5bc6fe0f1d51ae47 127.0.0.1:7003 replicates 0832bbcd7d6843f7da8c38a8683293b58e12fbc2 S: f12f469b0489b51cee4d1748a489cf6b21766853 127.0.0.1:7004 replicates 6eeea311f17374abebde4dff97fa957db8b3ddd3 S: 39baf1cdb1afec29f8a1005db841e262d3b6b7da 127.0.0.1:7005 replicates a7856400b2e82b8d18fe64d908a4f9cd46dbad41 [OK] All nodes agree about slots configuration.
Check for open slots… Check slots coverage… [OK] All 16384 slots covered. ```
- 验证测试
```bash
[root@server1 ~]# 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:112 cluster_stats_messages_pong_sent:114 cluster_stats_messages_sent:226 cluster_stats_messages_ping_received:109 cluster_stats_messages_pong_received:112 cluster_stats_messages_meet_received:5 cluster_stats_messages_received:226
查询各集群节点
127.0.0.1:7000> cluster nodes 0832bbcd7d6843f7da8c38a8683293b58e12fbc2 127.0.0.1:7002@17002 master - 0 1616410133502 3 connected 10923-16383 690b1befaa5580005da180a13428c836a0eae3b5 127.0.0.1:7001@17001 master - 0 1616410133000 2 connected 5461-10922 9e2f1c705ee048b9894f0b602301eb2d41328866 127.0.0.1:7005@17005 slave 08527700d9fbe3f5e554dd33a1137004b0fc7783 0 1616410135000 6 connected 770468e89568cbc2ab84a909217fa4abfee3b8d1 127.0.0.1:7003@17003 slave 690b1befaa5580005da180a13428c836a0eae3b5 0 1616410134609 4 connected 2b9299ae7450c2a13f443f6914f380cb71533ae8 127.0.0.1:7004@17004 slave 0832bbcd7d6843f7da8c38a8683293b58e12fbc2 0 1616410135113 5 connected 08527700d9fbe3f5e554dd33a1137004b0fc7783 127.0.0.1:7000@17000 myself,master - 0 1616410133000 1 connected 0-5460
数据分配
redis 127.0.0.1:7000> set foo bar -> Redirected to slot [12182] located at 127.0.0.1:7002 OK redis 127.0.0.1:7002> set hello world -> Redirected to slot [866] located at 127.0.0.1:7000 OK redis 127.0.0.1:7000> get foo -> Redirected to slot [12182] located at 127.0.0.1:7002 “bar” redis 127.0.0.1:7002> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 “world” ```
