redis要点

Redis

1、简介

企业缓存产品介绍

  • Tair
# 优势:
    高性能读写、支持三种存储引擎(ddb、rdb、ldb)、支持高可用、支持分布式分片集群、支撑了几乎全部淘宝业务的缓存。

# 缺点:
    单机状况下,读写性能较其余两种产品较慢
  • Memcached
# 优势:
    高性能读写、单一数据类型、支持客户端式分布式集群、一致性hash多核结构、多线程读写性能高。
    
# 缺点:
    无持久化、节点故障可能出现缓存穿透、分布式须要客户端实现、跨机房数据同步困难、架构扩容复杂度高
  • Redis
# 优势:
    高性能读写、多数据类型支持、数据持久化、高可用架构、支持自定义虚拟内存、支持分布式分片集群、单线程读写性能极高
  
# 缺点:
    多线程读写较Memcached慢。通常是新浪、京东、直播类平台、网页游戏使用
    
# memcache与redis在读写性能的对比:
memcached:多核的缓存服务,更加适合于多用户并发访问次数较少的应用场景
Redis:单核的缓存服务,单节点状况下,更加适合于少许用户,屡次访问的应用场景。Redis通常是单机多实例架构,配合Redis集群出现。

Redis功能介绍

  • 高速读写
  • 数据类型丰富 (笔试、面试)
  • 支持持久化 (笔试、面试)
  • 多种内存分配及回收策略
  • 支持事务 (面试)
  • 消息队列、消息订阅
  • 支持高可用
  • 支持分布式分片集群 (面试)
  • 缓存穿透\雪崩(笔试、面试)
  • Redis API

2、Redis安装

# 下载:
wget http://download.redis.io/releases/redis-3.2.12.tar.gz
# 解压:
上传至 /data
tar xzf redis-3.2.12.tar.gz
mv redis-3.2.12 redis
# 安装:
cd redis
# 编译
make
# 启动:
src/redis-server &


# 配置环境变量:
vim /etc/profile 
export PATH=/data/redis/src:$PATH
source /etc/profile 


# 开启服务端
redis-server & 
# 开启客户端
redis-cli 

# 测试
127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> get num
10

3、Redis基本管理操做

1.基础配置文件介绍

# 配置
mkdir /data/6379

cat >>/data/6379/redis.conf <<EOF
daemonize yes                   # 是否后台运行
port 6379                       # 默认端口
logfile /data/6379/redis.log    # 日志文件位置
dir /data/6379                  # 持久化文件存储位置
dbfilename dump.rdb             # RDB持久化数据文件
EOF

# 重启redis
# 关闭客户端链接
redis-cli shutdown 
# 以配置文件启动
redis-server /data/6379/redis.conf 
# 检测端口
netstat -lnp|grep 63


# 开启客户端
redis-cli 

# 测试
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> get name
"zhangsan"

2.客户端命令经常使用参数说明

# redis-cli 刚装完,能够在redis服务器上直接登陆redis
-p 6379   指定端口号
-h        指定连接地址
-a        指定连接密码

# 无交互执行redis命令,不用进入redis也可操做,可是若是由密码便须要密码
redis-cli  set num  10

3.远程链接

# redis默认开启了保护模式,只容许本地回环地址登陆并访问数据库。
protected-mode yes/no (保护模式,是否只容许本地访问)

# 1.Bind :指定IP进行监听
echo "bind 10.0.0.200  127.0.0.1" >>/data/6379/redis.conf
# 2.增长requirepass  {password}
echo "requirepass 123" >>/data/6379/redis.conf

# 重启redis
redis-cli shutdown 
redis-server /data/6379/redis.conf 


# 验证密码
# 方法一:
[root@db03 ~]# redis-cli -a 123
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> exit
    
# 方法二:
[root@db03 ~]# redis-cli
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> set a b

    
    
# 在线查看和修改配置
CONFIG GET *
CONFIG GET requirepass
CONFIG SET requirepass 123

四.redis持久化(内存数据保存到磁盘)

1.做用

​ 能够有效防止,在redis宕机后,缓存失效的问题.python

2.持久化方式

  • RDB
能够在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
    
优势:
    速度快,适合于用作备份,主从复制也是基于RDB持久化功能实现的。
缺点:
    会有数据丢失
  • AOF
记录服务器执行的全部写操做命令,并在服务器启动时,经过从新执行这些命令来还原数据集。 
    AOF 文件中的命令所有以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。
    
优势:
    能够最大程度保证数据不丢
缺点:
    日志记录量级比较大,会显得结构臃肿

3.RDB

# rdb持久化核心配置参数:

vim /data/6379/redis.conf

dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

# 配置分别表示:
900秒(15分钟)内有1个更改
300秒(5分钟)内有10个更改
60秒内有10000个更改

# 也就是说一分钟以内10000个变更
# 或者5分钟10次
# 或15分钟1次才会持久化一次
# 这样就会致使一个问题,数据落地会不及时,有延迟,数据丢失

4.AOF

# AOF持久化配置
vim /data/6379/redis.conf
appendonly yes
appendfsync everysec # evertsec/always/no


# everysec:异步操做,每秒记录,若是一秒钟内宕机,有数据丢失
# always:同步持久化,每次发生数据变动会被当即记录到磁盘,性能差但数据完整性比较好
# no:将缓存回写的策略交给系统,linux 默认是30秒将缓冲区的数据回写硬盘的


# AOF的Rewrite(重写) : 
  定义:AOF采用文件追加的方式持久化数据,因此文件会愈来愈大,为了不这种状况发生,增长了重写机制 
  当AOF文件的大小超过了配置所设置的阙值时,Redis就会启动AOF文件压缩,只保留能够恢复数据的最小指令集,可使用命令bgrewriteaof 
触发机制:Redis会记录上次重写时的AOF文件大小,默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发

auto-aof-rewrite-percentage 100 (一倍) 
auto-aof-rewrite-min-size 64mb 





# 面试: 
redis 持久化方式有哪些?有什么区别?

rdb:基于快照的持久化,速度更快,通常用做备份,主从复制也是依赖于rdb持久化功能
aof:以追加的方式记录redis操做日志的文件。能够最大程度的保证redis数据安全,相似于mysql的binlog

5、Redis数据类型

1.介绍

# string:字符型
键      值
key     value

# hash:字典类型
key     col1 value1  col2 value2 

# list:列表
key     [a,b,c,d]  
         0 1 2 3
    
# set:集合
key1    (a,b,c,d) 

# sorted set:有序集合
          10     20   30  40 
key1     ( a,    b,   c,  d)  
           0     1    2   3

2.通用操做

# 查看
# 查全部,数据量大时通常不推荐使用
KEYS *   
# 查单个 
keys 健名  
# 模糊查询
keys a* 

# 返回键所存储值的类型 
TYPE

# 检查是否存在   
EXISTS

# 以秒\毫秒设定生存时间  
EXPIRE\ PEXPIRE

# 以秒\毫秒为单位返回生存时间
TTL\ PTTL

# 取消生存实现设置 
PERSIST

# 删除一个key
DEL     

# 变动KEY名
RENAME

3.string

# 应用场景
常规计数:
微博数,粉丝数等。
订阅、礼物、页游
key:value
----------
#(1)
set name zhangsan   
#(2)
MSET id 101 name zhangsan age 20 gender m
# 等价于如下操做:
SET id 101 
set name zhangsan 
set age 20 
set gender m
 
mget id name age gender

#(3)计数器
每点一次关注,都执行如下命令一次
127.0.0.1:6379> incr fans_count
(integer) 10003
127.0.0.1:6379> get fans_count
"10003"
127.0.0.1:6379> incrby fans_count 1000
(integer) 11003
127.0.0.1:6379> decr fans_count 
(integer) 11002
127.0.0.1:6379> decrby  fans_count 1000

# 详细的例子
# 增
set    mykey "test"             # 为键设置新值,并覆盖原有值
getset mycounter 0              # 设置值,取值同时进行
setex  mykey 10 "hello"         # 设置指定 Key 的过时时间为10秒,在存活时间能够获取value
setnx mykey "hello"             # 若该键不存在,则为键设置新值
mset key3  "zyx"  key4 "xyz"    # 批量设置键
---------------------------


# 删
del mykey                       # 删除已有键
---------------------------


# 改
append mykey "hello"            # 若该键并不存在,返回当前 Value 的长度;该键已经存在,返回追加后 Value的长度
incr mykey                      # 值增长1,若该key不存在,建立key,初始值设为0,增长后结果为1
decrby  mykey  5                # 值减小5
setrange mykey 20 dd            # 把第21和22个字节,替换为dd, 超过value长度,自动补0
---------------------------


# 查  
exists mykey                    # 判断该键是否存在,存在返回 1,不然返回0
get mykey                       # 获取Key对应的value
strlen mykey                    # 获取指定 Key 的字符长度
ttl mykey                       # 查看一下指定 Key 的剩余存活时间(秒数)
getrange mykey 1 20             # 获取第2到第20个字节,若20超过value长度,则截取第2个和后面全部的
mget key3 key4                  # 批量获取键

4.hash

# 应用场景:
存储部分变动的数据,如用户信息等。
最接近mysql表结构的一种类型

--------------
# 存数据:
hmset stu id 101 name zs age 18

id    name     age
101   zs      18
# 同等与MySQL的
insert into stu(id,name,age) values (101,'zs',18);

# 取数据:
HMGET stu id name age     -----> select id,name,age from stu;
hgetall stu               ------> select * from stu;

# 将mysql中world.city表前10行数据导入redis

select concat("hmset city_",id," id ",id," name ",name," countrycode ",countrycode," district ",district," population ",population) from world.city  into outfile '/tmp/hmset1.txt'

cat /tmp/hmset.txt |redis-cli -a 123

hmset city_1  id 1 name Kabul contrycode AFG district Kabol population  1780000 


# 例子
# 增
hset myhash field1 "s"     # 若字段field1不存在,建立该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s ,若存在会覆盖原value
hsetnx myhash field1 s     # 若字段field1不存在,建立该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s, 若字段field1存在,则无效
hmset myhash field1 "hello" field2 "world"  # 一次性设置多个字段
---------------------------


# 删
hdel myhash field1                      # 删除 myhash 键中字段名为 field1 的字段
del myhash                              # 删除键
---------------------------


# 改  
hincrby myhash field 1                  # 给field的值加1
---------------------------


# 查
hget myhash field1              # 获取键值为 myhash,字段field1 的值
hlen myhash                     # 获取myhash键的字段数量
hexists myhash field1           # 判断 myhash 键中是否存在字段名为 field1 的字段
hmget myhash field1 field2 field3       # 一次性获取多个字段
hgetall myhash                  # 返回 myhash 键的全部字段及其值
hkeys myhash                    # 获取myhash 键中全部字段的名字
hvals myhash                    # 获取 myhash 键中全部字段的值

5.list

# 应用场景

消息队列系统
好比sina微博:在Redis中咱们的最新微博ID使用了常驻缓存,这是一直更新的。
可是作了限制不能超过5000个ID,所以获取ID的函数会一直询问Redis。
只有在start/count参数超出了这个范围的时候,才须要去访问数据库。
系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。
SQL数据库(或是硬盘上的其余类型数据库)只是在用户须要获取“很远”的数据时才会被触发,
而主页或第一个评论页是不会麻烦到硬盘上的数据库了。


# 微信朋友圈:

127.0.0.1:6379> LPUSH wechat "today is nice day !"

127.0.0.1:6379> LPUSH wechat "today is bad day !"

127.0.0.1:6379> LPUSH wechat "today is good  day !"

127.0.0.1:6379> LPUSH wechat "today is rainy  day !"

127.0.0.1:6379> LPUSH wechat "today is friday !"

[e,d,c,b,a]
 0 1 2 3  4

127.0.0.1:6379> lrange wechat  0 0
"today is friday !"
127.0.0.1:6379> lrange wechat  0 1
"today is friday !"
"today is rainy  day !"
127.0.0.1:6379> lrange wechat  0 2
"today is friday !"
"today is rainy  day !"
"today is good  day !"
127.0.0.1:6379> lrange wechat  0 3
127.0.0.1:6379> lrange wechat  -2 -1
"today is bad day !"
"today is nice day !"


-----------------
# 增 
lpush mykey a b             # 若key不存在,建立该键及与其关联的List,依次插入a ,b, 若List类型的key存在,则插入value中
lpushx mykey2 e             # 若key不存在,此命令无效, 若key存在,则插入value中
linsert mykey before a a1   # 在 a 的前面插入新元素 a1
linsert mykey after e e2    # 在e 的后面插入新元素 e2
rpush mykey a b             # 在链表尾部先插入b,在插入a
rpushx mykey e              # 若key存在,在尾部插入e, 若key不存在,则无效
rpoplpush mykey mykey2      # 将mykey的尾部元素弹出,再插入到mykey2 的头部(原子性的操做)
---------------------------


# 删
del mykey                   # 删除已有键 
lrem mykey 2 a              # 从头部开始找,按前后顺序,值为a的元素,删除数量为2个,若存在第3个,则不删除
ltrim mykey 0 2             # 从头开始,索引为0,1,2的3个元素,其他所有删除
---------------------------


# 改
lset mykey 1 e              # 从头开始, 将索引为1的元素值,设置为新值 e,若索引越界,则返回错误信息
rpoplpush mykey mykey       # 将 mykey 中的尾部元素移到其头部
---------------------------


# 查
lrange mykey 0 -1           # 取链表中的所有元素,其中0表示第一个元素,-1表示最后一个元素。
lrange mykey 0 2            # 从头开始,取索引为0,1,2的元素
lrange mykey 0 0            # 从头开始,取第一个元素,从第0个开始,到第0个结束
lpop mykey                  # 获取头部元素,而且弹出头部元素,出栈
lindex mykey 6              # 从头开始,获取索引为6的元素 若下标越界,则返回nil 

------------

6.set

# 应用场景:
案例:在微博应用中,能够将一个用户全部的关注人存在一个集合中,将其全部粉丝存在一个集合。
Redis还为集合提供了求交集、并集、差集等操做,能够很是方便的实现如共同关注、共同喜爱、二度好友等功能,
对上面的全部集合操做,你还可使用不一样的命令选择将结果返回给客户端仍是存集到一个新的集合中。

-------------
# lxl的好友
127.0.0.1:6379> sadd lxl pg1 pg2 songlaoban oldnie oldchen marong
(integer) 6

# jnl的好友
127.0.0.1:6379> sadd jnl baoqiang yufan oldchen songzhe oldguo  alexdsb 
(integer) 2

# lxl jnl全部的好友
127.0.0.1:6379> SUNION lxl jnl
 1) "marong"
 2) "pg2"
 3) "pg1"
 4) "oldchen"
 5) "alexdsb"
 6) "yufan"
 7) "songlaoban"
 8) "baoqiang"
 9) "oldnie"
10) "songzhe"
11) "oldguo"

# 共同的好友
127.0.0.1:6379> SINTER lxl jnl
1) "oldchen"


#  lxl的但不是jnl的好友
127.0.0.1:6379> SDIFF lxl jnl
1) "songlaoban"
2) "oldnie"
3) "pg1"
4) "pg2"
5) "marong"
# jnl的但不是lxl的好友
127.0.0.1:6379> SDIFF  jnl lxl 
1) "alexdsb"
2) "yufan"
3) "songzhe"
4) "oldguo"
5) "baoqiang"
-----------------
# 增
sadd myset a b c            # 若key不存在,建立该键及与其关联的set,依次插入a ,b,若key存在,则插入value中,若a 在myset中已经存在,则插入了 d 和 e 两个新成员。
---------------------------


# 删
spop myset                  # 尾部的b被移出,事实上b并非以前插入的第一个或最后一个成员
srem myset a d f            # 若f不存在, 移出 a、d ,并返回2
---------------------------


# 改
smove myset myset2 a        # 将a从 myset 移到 myset2,
---------------------------


# 查
sismember myset a           # 判断 a 是否已经存在,返回值为 1 表示存在。
smembers myset              # 查看set中的内容
scard myset                 # 获取Set 集合中元素的数量
srandmember myset           # 随机的返回某一成员
sdiff myset1 myset2 myset3  # 1和2获得一个结果,拿这个集合和3比较,得到每一个独有的值
sdiffstore diffkey myset myset2 myset3    # 3个集和比较,获取独有的元素,并存入diffkey 关联的Set中
sinter myset myset2 myset3  # 得到3个集合中都有的元素
sinterstore interkey myset myset2 myset3  # 把交集存入interkey 关联的Set中
sunion myset myset2 myset3  # 获取3个集合中的成员的并集
sunionstore unionkey myset myset2 myset3  # 把并集存入unionkey 关联的Set中

7.SortedSet

# 应用场景:
排行榜应用,取TOP N操做

这个需求与上面需求的不一样之处在于,前面操做以时间为权重,这个是以某个条件为权重,好比按顶的次数排序,
这时候就须要咱们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,
每次只须要执行一条ZADD命令便可。

--------------
127.0.0.1:6379> zadd topN 0 smlt 0 fskl 0 fshkl 0 lzlsfs 0 wdhbx 0 wxg 
(integer) 6
127.0.0.1:6379> ZINCRBY topN 100000 smlt
"100000"
127.0.0.1:6379> ZINCRBY topN 10000 fskl
"10000"
127.0.0.1:6379> ZINCRBY topN 1000000 fshkl
"1000000"
127.0.0.1:6379> ZINCRBY topN 100 lzlsfs
"100"
127.0.0.1:6379> ZINCRBY topN 10 wdhbx
"10"
127.0.0.1:6379> ZINCRBY topN 100000000 wxg
"100000000"

127.0.0.1:6379> ZREVRANGE topN 0 2 
1) "wxg"
2) "fshkl"
3) "smlt"
127.0.0.1:6379> ZREVRANGE topN 0 2 withscores
1) "wxg"
2) "100000000"
3) "fshkl"
4) "1000000"
5) "smlt"
6) "100000"
127.0.0.1:6379> 

--------------
# 增
zadd myzset 2 "two" 3 "three"       # 添加两个分数分别是 2 和 3 的两个成员
---------------------------


# 删
zrem myzset one two                 # 删除多个成员变量,返回删除的数量
---------------------------


# 改
zincrby myzset 2 one                # 将成员 one 的分数增长 2,并返回该成员更新后的分数
---------------------------


# 查 
zrange myzset 0 -1 WITHSCORES       # 返回全部成员和分数,不加WITHSCORES,只返回成员
zrank myzset one                    # 获取成员one在Sorted-Set中的位置索引值。0表示第一个位置
zcard myzset                        # 获取 myzset 键中成员的数量
zcount myzset 1 2                   # 获取分数知足表达式 1 <= score <= 2 的成员的数量
zscore myzset three                 # 获取成员 three 的分数
zrangebyscore myzset  1 2           # 获取分数知足表达式 1 < score <= 2 的成员


#-inf 表示第一个成员,+inf最后一个成员
#limit限制关键字
#2  3  是索引号
zrangebyscore myzset -inf +inf limit 2 3  # 返回索引是2和3的成员
zremrangebyscore myzset 1 2         # 删除分数 1<= score <= 2 的成员,并返回实际删除的数量
zremrangebyrank myzset 0 1              # 删除位置索引知足表达式 0 <= rank <= 1 的成员
zrevrange myzset 0 -1 WITHSCORES        # 按位置索引从高到低,获取全部成员和分数
#原始成员:位置索引从小到大
      one  0  
      two  1
#执行顺序:把索引反转
      位置索引:从大到小
      one 1
      two 0
#输出结果: two  
      one
zrevrange myzset 1 3                # 获取位置索引,为1,2,3的成员
#相反的顺序:从高到低的顺序
zrevrangebyscore myzset 3 0             # 获取分数 3>=score>=0的成员并以相反的顺序输出
zrevrangebyscore myzset 4 0 limit 1 2   # 获取索引是1和2的成员,并反转位置索引

6、redis发布消息

1.介绍

Redis发布消息一般有两种模式:mysql

  • 队列模式(queuing)linux

    任务队列的好处:面试

    ​ 松耦合。redis

    ​ 易于扩展。算法

  • 发布-订阅模式(publish-subscribe)sql

其实从Pub/Sub的机制来看,它更像是一个广播系统,多个Subscriber能够订阅多个Channel,多个Publisher能够往多个Channel中发布消息。能够这么简单的理解:

    Subscriber:收音机,能够收到多个频道,并以队列方式显示

    Publisher:电台,能够往不一样的FM频道中发消息

    Channel:不一样频率的FM频道
    
    
客户端在执行订阅命令以后进入了订阅状态,只能接收 SUBSCRIBE 、PSUBSCRIBE、 UNSUBSCRIBE 、PUNSUBSCRIBE 四个命令。 开启的订阅客户端,没法收到该频道以前的消息,由于 Redis 不会对发布的消息进行持久化。 和不少专业的消息队列系统(例如Kafka、RocketMQ)相比,Redis的发布订阅略显粗糙,例如没法实现消息堆积和回溯。但胜在足够简单,若是当前场景能够容忍的这些缺点,也不失为一个不错的选择。

2.发布订阅

# 将信息 message 发送到指定的频道 channel
PUBLISH channel msg

# 订阅频道,能够同时订阅多个频道
SUBSCRIBE channel [channel ...]
    
# 取消订阅指定的频道, 若是不指定频道,则会取消订阅全部频道
UNSUBSCRIBE [channel ...]
    
# 订阅一个或多个符合给定模式的频道,每一个模式以 * 做为匹配符,好比 it* 匹配所 有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配全部 以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类
PSUBSCRIBE pattern [pattern ...]
    
# 退订指定的规则, 若是没有参数则会退订全部规则
PUNSUBSCRIBE [pattern [pattern ...]]

# 查看订阅与发布系统状态
PUBSUB subcommand [argument [argument ...]]
    
# 注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,以前发送的不会缓存,必须Provider和Consumer同时在线。
# 发布订阅例子:

窗口1:
# 先要订阅频道baodi
127.0.0.1:6379> SUBSCRIBE baodi 

窗口2:
# 频道发布信息,订阅者会立马收到
127.0.0.1:6379> PUBLISH baodi "jin tian zhen kaixin!"

订阅多频道:
窗口1:
127.0.0.1:6379> PSUBSCRIBE wang*
窗口2:
127.0.0.1:6379> PUBLISH wangbaoqiang "jintian zhennanshou "

7、redis事务

redis的事务是基于队列实现的。
mysql的事务是基于事务日志实现的。

----------
# 开启事务功能时(multi)
multi 
command1      
command2
command3
command4

# 4条语句做为一个组,并无真正执行,而是被放入同一队列中。
若是,这是执行discard,会直接丢弃队列中全部的命令,而不是作回滚。

exec
# 当执行exec时,对列中全部操做,要么全成功要么全失败


----------
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a b
QUEUED
127.0.0.1:6379> set c d
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK

8、redis主从复制

1.简介

持久化保证了即便redis服务重启也不会丢失数据,由于redis服务重启后会将硬盘上持久化的数据恢复到内存中,可是当redis服务器的硬盘损坏了可能会致使数据丢失,若是经过redis的主从复制机制就能够避免这种单点故障数据库

2.原理

  1. 从服务器向主服务器发送 SYNC 命令。
  2. 接到 SYNC 命令的主服务器会调用BGSAVE 命令,建立一个 RDB 文件,并使用缓冲区记录接下来执行的全部写命令。
  3. 当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件。
  4. 主服务器将缓冲区储存的全部写命令(广播形式)发送给从服务器执行。

主服务器会一直持续第四步,只要有新的命令,主服务器会监视vim

3.主从的持久化

当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。 不然的话,因为延迟等问题,部署的服务应该要避免自动拉起。

为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考一下如下会致使主从服务器数据所有丢失的例子:

  1. 假设节点A为主服务器,而且关闭了持久化。 而且节点B和节点C从节点A复制数据
  2. 节点A崩溃,而后由自动拉起服务重启了节点A. 因为节点A的持久化被关闭了,因此重启以后没有任何数据
  3. 节点B和节点C将从节点A复制数据,可是A的数据是空的, 因而就把自身保存的数据副本删除。

在关闭主服务器上的持久化,并同时开启自动拉起进程的状况下,即使使用Sentinel来实现Redis的高可用性,也是很是危险的。 由于主服务器可能拉起得很是快,以致于Sentinel在配置的心跳时间间隔内没有检测到主服务器已被重启,而后仍是会执行上面的数据丢失的流程。

不管什么时候,数据安全都是极其重要的,因此应该禁止主服务器关闭持久化的同时自动拉起。

# 给主服务器加上持久化
appendonly yes
appendfsync everysec

4.复制的一致性

  1. 在读写分离环境下,客户端向主服务器发送写命令 SET n 10086,主服务器在执行这个写命令以后,向客户端返回回复,并将这个写命令传播给从服务器。
  2. 接到回复的客户端继续向从服务器发送读命令 GET n ,而且由于网络状态的缘由,客户端的 GET命令比主服务器传播的 SET 命令更快到达了从服务器。
  3. 由于从服务器键 n 的值还未被更新,因此客户端在从服务器读取到的将是一个错误(过时)的 n值。

4.1解决方案

# 主从数据一致性保证:
# 在配置文件中配置
min-slaves-to-write 1   # 从服务器的数量
min-slaves-max-lag  2   # 网络延迟的最大秒数


这个特性的运做原理:
从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理状况。
主服务器会记录各个从服务器最后一次向它发送 PING 的时间。

用户能够经过配置, 指定网络延迟的最大值 min-slaves-max-lag ,

以及执行写操做所需的至少从服务器数量 min-slaves-to-write 。

若是至少有 min-slaves-to-write 个从服务器, 而且这些服务器的延迟值都少于 min-slaves-max-lag秒,

那么主服务器就会执行客户端请求的写操做。

你能够将这个特性看做 CAP 理论中的 C 的条件放宽版本: 尽管不能保证写操做的持久性, 
但起码丢失数据的窗口会被严格限制在指定的秒数中。

另外一方面, 若是条件达不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的条件, 那么写操做就不会被执行
主服务器会向请求执行写操做的客户端返回一个错误。

5.主从复制实现

# 一、环境:
# 准备两个或两个以上redis实例

mkdir /data/638{0..2}

# 配置文件示例:
cat >> /data/6380/redis.conf << EOF 
port 6380
daemonize yes
pidfile /data/6380/redis.pid
loglevel notice
logfile "/data/6380/redis.log"
dbfilename dump.rdb
dir /data/6380
requirepass 123
masterauth 123
appendonly yes
appendfsync everysec
EOF

cp /data/6380/redis.conf /data/6381/redis.conf
cp /data/6380/redis.conf /data/6382/redis.conf

sed -i 's#6380#6381#g' /data/6381/redis.conf
sed -i 's#6380#6382#g' /data/6382/redis.conf


# 启动:
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf

# 查看端口
netstat -lnp|grep 638
 
 
主节点:6380
从节点:638一、6382

# 二、开启主从:
6381/6382命令行:

redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380
redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380


# 三、查询主从状态
redis-cli -p 6380 -a 123 info replication
redis-cli -p 6381 -a 123 info replication
redis-cli -p 6382 -a 123 info replication


# 模拟主库故障
redis-cli -p 6380 -a 123 shutdown

# 1.从库切为主库
redis-cli -p 6381 -a 123
info replication
slaveof no one

# 2.6382链接到6381:
redis-cli -p 6382 -a 123
127.0.0.1:6382> SLAVEOF no one
127.0.0.1:6382> SLAVEOF 127.0.0.1 6381

6.redis-sentinel(哨兵)

6.1 功能

  1. 监控
  2. 自动选主,切换(6381 slaveof no one)
  3. 2号从库(6382)指向新主库(6381)
  4. 应用透明

当主服务器挂掉时,能够快速切换主从,尽快恢复服务

6.2 sentinel搭建过程

# 1.建立哨兵文件夹
mkdir /data/26380
# 2.配置哨兵
cat >> /data/26380/sentinel.conf << EOF
port 26380
dir "/data/26380"
sentinel monitor mymaster 127.0.0.1 6380 1
sentinel down-after-milliseconds mymaster 5000
sentinel auth-pass mymaster 123 
EOF
# 3.启动:
redis-sentinel /data/26380/sentinel.conf &


# 配置详解

# 格式:sentinel <option_name> <master_name> <option_value>#该行的意思是:监控的master的名字叫作mymaster(自定义),地址为127.0.0.1:6380,行尾最后的一个1表明在sentinel集群中,多少个sentinel认为masters死了,才能真正认为该master不可用了。
sentinel monitor mymaster 127.0.0.1 6380 1


#sentinel会向master发送心跳PING来确认master是否存活,若是master在“必定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而这个down-after-milliseconds就是用来指定这个“必定时间范围”的,单位是毫秒,默认30秒。
sentinel down-after-milliseconds mymaster 5000

:此时一个哨兵就正式开启了,每次就经过哨兵访问便可

​ redis-cli -p 26380

​ 哨兵会自动指向主库,就算主库挂了,哨兵也会在内部根据实际状况选择一个主库,让剩下的从库指向新的主库,并且挂了的主库也会从新启动变成从库

6.3 Sentinel管理命令

PING :返回 PONG 。
SENTINEL masters :# 列出全部被监视的主服务器
SENTINEL slaves <master name>: # 列出全部被监视的从服务器
SENTINEL get-master-addr-by-name <master name> : # 返回给定名字的主服务器的 IP 地址和端口号。 
SENTINEL reset <pattern> : # 重置全部名字和给定模式 pattern 相匹配的主服务器。 
SENTINEL failover <master name> : # 当主服务器失效时, 在不询问其余 Sentinel 意见的状况下, 强制开始一次自动故障迁移。

9、redis集群

1.基本介绍

Redis集群是一个能够在多个 Redis 节点之间进行数据共享的设施installation

Redis 集群不支持那些须要同时处理多个键的 Redis 命令, 由于执行这些命令须要在多个 Redis 节点之间移动数据, 而且在高负载的状况下, 这些命令将下降Redis集群的性能, 并致使不可预测的行为。

Redis 集群经过分区partition来提供必定程度的可用性availability: 即便集群中有一部分节点失效或者没法进行通信, 集群也能够继续处理命令请求。

Redis集群提供了如下两个好处:

  • 将数据自动切分split到多个节点的能力。
  • 当集群中的一部分节点失效或者没法进行通信时, 仍然能够继续处理命令请求的能力。

2.Redis Cluster集群

Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0以后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每一个节点保存数据和整个集群状态,每一个节点都和其余全部 节点链接。

特色:

  1. 全部的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  2. 节点的fail是经过集群中超过半数的节点检测失效时才生效。
  3. 客户端与redis节点直连,不须要中间proxy层.客户端不须要链接集群全部节点,链接集群中任何一个可用节点便可。
  4. redis-cluster把全部的物理节点映射到[0-16383]slot上(不必定是平均分配),cluster 负责维护node<->slot<->value。
  5. Redis集群预分好16384个桶,当须要在 Redis 集群中放置一个 key-value 时,根据 CRC16(key) mod 16384的值,决定将一个key放到哪一个桶中。

3. Redis Cluster节点分配

如今咱们是三个主节点分别是:A, B, C 三个节点,它们能够是一台机器上的三个端口,也能够是三台不一样的服务器。那么,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:

节点A覆盖0-5460;
节点B覆盖5461-10922;
节点C覆盖10923-16383.

获取数据:

​ 若是存入一个值'data'='1024-4-12',按照redis cluster哈希槽的算法: CRC16('data')%16384 = 2022。 那么就会把这个key 的存储分配到 A 上了。一样,当我链接(A,B,C)任何一个节点想获取'key'这个key时,也会这样的算法,而后内部跳转到A节点上获取数据

新增一个主节点:

新增一个节点D,redis cluster的这种作法是从各个节点的前面各拿取一部分slot到D上,我会在接下来的实践中实验。大体就会变成这样:
  

节点A覆盖1365-5460
节点B覆盖6827-10922
节点C覆盖12288-16383
节点D覆盖0-1364,5461-6826,10923-12287

4.Redis Cluster主从模式

​ redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。redis集群主从

​ 上面那个例子里, 集群有ABC三个主节点, 若是这3个节点都没有加入从节点,若是B挂掉了,咱们就没法访问整个集群了。A和C的slot也没法访问。

​ 因此咱们在集群创建的时候,必定要为每一个主节点都添加了从节点, 好比像这样, 集群包含主节点A、B、C, 以及从节点A一、B一、C1, 那么即便B挂掉系统也能够继续正确工做。

​ B1节点替代了B节点,因此Redis集群将会选择B1节点做为新的主节点,集群将会继续正确地提供服务。 当B从新开启后,它就会变成B1的从节点。

​ 不过须要注意,若是节点B和B1同时挂了,Redis集群就没法继续正确地提供服务了。

5.redis集群的搭建

# 6个redis实例,通常会放到3台硬件服务器
# 注:在企业规划中,一个分片的两个分到不一样的物理机,防止硬件主机宕机形成的整个分片数据丢失。

# 端口号:7000-7005

# 一、安装集群插件
EPEL源安装ruby支持
yum install ruby rubygems -y

# 可使用国内源
gem sources -l
gem sources -a http://mirrors.aliyun.com/rubygems/ 
gem sources  --remove https://rubygems.org/
gem sources -l
gem install redis -v 3.3.3

# 2.集群节点准备

mkdir /data/700{0..5}

cat >> /data/7000/redis.conf << EOF
port 7000
daemonize yes
pidfile /data/7000/redis.pid
loglevel notice
logfile "/data/7000/redis.log"
dbfilename dump.rdb
dir /data/7000
protected-mode no
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOF

cp /data/7000/redis.conf /data/7001/redis.conf
cp /data/7000/redis.conf /data/7002/redis.conf
cp /data/7000/redis.conf /data/7003/redis.conf
cp /data/7000/redis.conf /data/7004/redis.conf
cp /data/7000/redis.conf /data/7005/redis.conf

sed -i 's#7000#7001#g' /data/7001/redis.conf
sed -i 's#7000#7002#g' /data/7002/redis.conf
sed -i 's#7000#7003#g' /data/7003/redis.conf
sed -i 's#7000#7004#g' /data/7004/redis.conf
sed -i 's#7000#7005#g' /data/7005/redis.conf

# 启动节点:

redis-server /data/7000/redis.conf 
redis-server /data/7001/redis.conf 
redis-server /data/7002/redis.conf 
redis-server /data/7003/redis.conf 
redis-server /data/7004/redis.conf 
redis-server /data/7005/redis.conf 


# 查看端口是否启动
ps -ef |grep redis
root       8854      1  0 03:56 ?        00:00:00 redis-server *:7000 [cluster]     
root       8858      1  0 03:56 ?        00:00:00 redis-server *:7001 [cluster]     
root       8860      1  0 03:56 ?        00:00:00 redis-server *:7002 [cluster]     
root       8864      1  0 03:56 ?        00:00:00 redis-server *:7003 [cluster]     
root       8866      1  0 03:56 ?        00:00:00 redis-server *:7004 [cluster]     
root       8874      1  0 03:56 ?        00:00:00 redis-server *:7005 [cluster]  
                
                
                
# 3.将节点加入集群管理

redis-trib.rb create --replicas 1 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
                        
                        
# 4.集群状态查看

# 集群主节点状态
redis-cli -p 7000 cluster nodes | grep master
# 集群从节点状态
redis-cli -p 7000 cluster nodes | grep slave




# 五、集群节点管理
# 增长新的节点7006/7007
# 1.添加主节点:
# add-node是加入集群节点
# 127.0.0.1:7006为要加入的节点
# 127.0.0.1:7000 表示加入的集群的一个节点,用来辨识是哪一个集群,理论上那个集群的节点均可以。
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
        
# 2.转移slot(从新分片) 
# redis-cluster在新增节点时并未分配卡槽,须要咱们手动对集群进行从新分片迁移数据
redis-trib.rb reshard 127.0.0.1:7000
How many slots do you want to move (from 1 to 16384)? 4096
# 它提示咱们须要迁移多少slot到7006上,咱们平分16384个哈希槽给4个节点:16384/4 = 4096,咱们须要移动4096个槽点到7006上。
What is the receiving node ID? 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48
# 须要输入7006的节点id
Source node #1:all  
#  若是咱们不打算从特定的节点上取出指定数量的哈希槽,那么能够向redis-trib输入 all,这样的话, 集群中的全部主节点都会成为源节点,redis-trib从各个源节点中各取出一部分哈希槽,凑够4096个,而后移动到7006节点上


# 添加一个从节点
# 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48为要加到master主节点的node id,127.0.0.1:7007为新增的从节点,127.0.0.1:7000为集群的一个节点(集群的任意节点都行),用来辨识是哪一个集群;若是没有给定那个主节点--master-id的话,redis-trib将会将新增的从节点随机到从节点较少的主节点上。
redis-trib.rb add-node --slave --master-id 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48 127.0.0.1:7007 127.0.0.1:7000


# 6.删除节点

# 将须要删除节点slot移动走
redis-trib.rb reshard 127.0.0.1:7000


# 删除一个节点
# 删除master节点以前首先要使用reshard移除master的所有slot,而后再删除当前节点
redis-trib.rb del-node 127.0.0.1:7006 1c98b2b2ce18f88c76821cdb82dba4defaa5eb48
redis-trib.rb del-node 127.0.0.1:7007 00185d1cf069b23468d5863202ac651f0d02a9f8
---------------------
# 设置redis最大内存
config set maxmemory 102400000
---------------------

10、python实现redis多种API

一、对redis的单实例进行链接操做

# redis单例提供了一种数据缓存方式和丰富的数据操做api,可是将数据彻底存储在单个redis中主要存在两个问题:数据备份和数据体量较大形成的性能下降。这里redis的主从模式为这两个问题提供了一个较好的解决方案。

# 安装
unzip redis-py-master.zip
cd redis-py-master
python3 setup.py install

# 使用
>>>import redis
>>>r = redis.StrictRedis(host='10.0.0.200', port=6379, db=0,password='123')
>>>r.set('foo', 'bar')
True
>>>r.get('foo')
'bar'

2.python使用redis的哨兵sentinel

# redis主从模式解决了数据备份和单例可能存在的性能问题,可是其也引入了新的问题。
# 因为主从模式配置了三个redis实例,而且每一个实例都使用不一样的ip(若是在不一样的机器上)和端口号。
# 根据前面所述,主从模式下能够将读写操做分配给不一样的实例进行从而达到提升系统吞吐量的目的,但也正是由于这种方式形成了使用上的不便,由于每一个客户端链接redis实例的时候都是指定了ip和端口号的,若是所链接的redis实例由于故障下线了,而主从模式也没有提供必定的手段通知客户端另外可链接的客户端地址,于是须要手动更改客户端配置从新链接。
# 另外,主从模式下,若是主节点因为故障下线了,那么从节点由于没有主节点而同步中断,于是须要人工进行故障转移工做。
# 为了解决这两个问题,在2.8版本以后redis正式提供了sentinel(哨兵)架构。

# 1.启动redis的实例以及sentinel
redis-server /data/6380/redis.conf
redis-server /data/6381/redis.conf
redis-server /data/6382/redis.conf 
redis-sentinel /data/26380/sentinel.conf &


# 使用
# 2.导入redis sentinel包
>>>from redis.sentinel import Sentinel  
# 指定sentinel的地址和端口号
>>> sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1)  
# 测试,获取如下主库和从库的信息
>>> sentinel.discover_master('mymaster')  
>>> sentinel.discover_slaves('mymaster')  

# 配置读写分离
# 写节点
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1,password="123")  
# 读节点
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1,password="123")  
# 读写分离测试   key     
>>> master.set('oldboy', '123')  
>>> slave.get('oldboy')  
'123'

2.redis cluster集群的链接并操做

# redis集群是在redis 3.0版本推出的一个功能,其有效的解决了redis在分布式方面的需求。
# 当遇到单机内存,并发和流量瓶颈等问题时,可采用Cluster方案达到负载均衡的目的。
# 而且从另外一方面讲,redis中sentinel有效的解决了故障转移的问题,也解决了主节点下线客户端没法识别新的可用节点的问题,可是若是是从节点下线了,sentinel是不会对其进行故障转移的,而且链接从节点的客户端也没法获取到新的可用从节点,而这些问题在Cluster中都获得了有效的解决。


# 安装redis-cluser的客户端程序
# 下载压缩包
cd redis-py-cluster-unstable
python3 setup.py install
# 或pip
pip install redis-py-cluster

# 使用
python3
>>> from rediscluster import StrictRedisCluster  
>>> startup_nodes = [{"host": "127.0.0.1", "port": "7000"},{"host": "127.0.0.1", "port": "7001"},{"host": "127.0.0.1", "port": "7002"}]   
>>> rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)  
>>> rc.set("name", "zs")  
True  
>>> print(rc.get("name"))  
'zs'

11、redis常见问题以及解决方案

# 缓存穿透
概念
访问一个不存在的key,缓存不起做用,请求会穿透到DB,流量大时DB会挂掉。

# 解决方案
采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤;
访问key未在DB查询到值,也将空值写进缓存,但能够设置较短过时时间。

# 缓存雪崩
概念
大量的key设置了相同的过时时间,致使在缓存在同一时刻所有失效,形成瞬时DB请求量大、压力骤增,引发雪崩。

# 解决方案
能够给缓存设置过时时间时加上一个随机值时间,使得每一个key的过时时间分布开来,不会集中在同一时刻失效。

# 缓存击穿
概念
一个存在的key,在缓存过时的一刻,同时有大量的请求,这些请求都会击穿到DB,形成瞬时DB请求量大、压力骤增。

# 解决方案
在访问key以前,采用SETNX(set if not exists)来设置另外一个短时间key来锁住当前key的访问,访问结束再删除该短时间key。
相关文章
相关标签/搜索