非关系型数据库,结构上key-value存储。
redisdoc.com # 在线的命令手册
memcache,是纯内存型数据库。redis是内存+磁盘。2者全部数据都是保存在内存中。
redis3.0开始支持redis cluster ,去中心化。任意节点进行读写操作都可以。
支持持久化存储、支持事务、支持主从、支持集群
1、redis使用场景
- 数据缓存
- session共享(会话保持)
- 计数器
redis+lua:lua作为封禁IP,redis作为计数。
- 消息队列
2、redis安全机制
bind 0.0.0.0 设置允许远程登录
protected-mode yes 保护模式,yes启用
requirepass 123456 登录密码,和上面配合使用
auth 123456 # 进行认证
3、redis持久化
3.1、RDB
数据文件:rdb文件,基于快照方式实现。将内存数据原封不动保存一遍。
缺点:会丢失持久化过程中的问题。
rdb实现方式的2种方式
1)、save
立刻执行,同步,阻塞写入。
缺点:持久化时候redis服务阻塞,不能对外提供服务。最短间隔只能是1min。
2)、bgsave
异步、非阻塞。
fork+copyonwrite
优点:可以一边持久化一边读写,不影响对外服务。
fork()是操作系统的api,非redis的api。fork创建出一个子进程进行持久化rdb操作,共享父类的内存数据,仅仅是那一刻的内存数据,后期主进程修改对子进程不可见。将快照内容写入一个临时文件,写入完成将改名为dump.rdb。
参数配置
save
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error
启用了rdb且最后一次后台保存数据失败,redis停止持久化操作。好处是让人发现数据没有正确持久化到磁盘上,否则不会引起注意。
rdbcompression
对于存储在磁盘中的快照,设置是否进行压缩。
rdbchecksum
在存储快照后,让redis使用CRC64算法进行数据校验,但是会增加10%的性能消耗。
3.2、AOF
aof更可靠。
appendfilename "appendonly.aof"
appendonly no # yes为启用
优点:最大限度保证数据不丢失,可精细化到每秒。主要是记录redis的操作。
缺点:文件会变的很大。bgrewrite会重建优化aof文件,删除原先的aof文件,保证aof文件不会太大。
aof参数配置
no-appendfsync-on-rewrite
在aof或者写入rdb时候,会执行大量IO,此时对于everysec和always模式时,执行fsync会阻塞,但是最安全,不会丢失数据;否则设置为yes,此时没有进行写入磁盘操作,而是写入内存。如果redis挂掉,会丢失30s的数据。
appendfsync
写入aof的频率:always(每次操作)、everysec(每秒)、no(不主动,系统判断写入)。
auto-aof-rewrite-percentage
何时重写:aof文件大小超过上次重写大小的百分之多少进行重写。默认100,二倍。
auto-aof-rewrite-min-size
允许重写的最小aof大小。避免达到约定百分比但是寸尺很小的情况吗,还需要重写。
aof-load-truncated
aof尾部发生损坏的时候,加载文件还是报错退出。
两种持久化策略,优先加载aof,因为其更可靠。
4、redis备份
备份持久化生成的rdb文件。
关闭rdb和aof
注释3个save,新增save ""
appendonly no
拷贝rdb。
5、配置文件详解
6、redis数据类型
1、字符串类型
set key value [ex seconds] [NX|XX] 新增
可选参数:
- ex seconds:过期时间 秒
- NX 键不存在,才设置
- XX 键已存在,才设置
append key value1 # 拼接存在的value
set name rose
append name 22
get name =>rose22
INCR num 自增+1
decr 自减-1
incrby key 20 自增+20
set age 2
INCR age => age => 3
mset key1 value1 key2 value2... 批量设置
mget key1 key2... 批量获取
strlen key 获取字符个数
type key 查看类型
2、哈希表类型 hash
hset key field value //新增 hset userinfo name jack
hget key field // 查询一个
hgetall key // 查询全部
hmset\hmget 批量新增和查询
3、列表类型 list
双向列表,可以实现消息队列的功能。利用push存入list,pop从列表中取出。
rpush key value # 定值推到列表右侧
lpush key value # 定值推到列表左侧
rpop key # 列表右端弹出值
lpop key # 列表左端弹出值
lrange start stop # 取范围值,-1 最后一位 # lrange 0 -1
lindex key index # 下标取值,0开始
lrem list1 -2 "tom" # 从尾部移除2个tom 0为移除全部
4、无序集合set
集合成员不可重复且无序的;集合是通过hash table实现的,添加、删除、查找复杂度都是O(1)`
5、zset
7、redis主从复制
redis提供主从复制模式,保证多台服务器数据一致性,且主从 服务器之间采用读写分离
的方式。
主服务器可以进行【读写】操作,而从服务器一般是只读(slave-read-only来控制),并接受主服务器的【数据同步】。且数据复制是单向的。
一个主服务器可以有多个从服务器,一个从服务器只能有一个主服务器,不支持主主复制。从服务器可以作为主服务器,下分组成从服务器。
复制方式
- 全量复制:第一次同步时
- 增量复制:只会把主从库网络断开期间主库收到的命令,同步给从库
2.8版本之前只有全量复制,只会才有增量复制。
全量复制
1、确定主从关系
replicaof host port (5.0之前使用salveof)命令让一个redis服务成为另一个redis服务的从库。
replicaof 192.168.1.1 6379
2、全量复制的流程
1)、从库向主库发送psync ? -1命令,主动请求进行完整同步
这一步是主从库间建立连接、协商同步的过程,主要为全量复制做准备。这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库就开始同步。
从库给主库发送psync命令,表示要进行数据同步,主库根据这条命令启动复制。psync包含主库的runID和复制进度offset两个参数。
runID是redis启动时给每个实例创建的唯一ID,标识不同的redis实例,当从库和主库第一次复制时,不知道主库的runID,所以将runID设置为?,offset表示复制进度,主要为了增量复制服务,这里因为是全量复制,所以是-1。
2)、主库向从库发送FULLRESYNCm命令并带上2个参数,主库的runID和复制进度offset。
3)、主库执行bgsave命令,生成RDB文件,发送给从库。
从库接收到RDB文件,会先清空数据库,然后加载RDB文件。因为从库通过replicaof命令开始和主库同步前,可能保存了其他数据,避免影响,会先清空数据库。
4)、将发送过程中收到的指令存储到replication buffer中,发送给从库。
从库接收并载入rdb文件时,主库仍然在写入数据,主库准备了个缓存区,数据写入缓存区,从库载入完成,缓存区的数据推送过去。
如果主从库断线,就会进行重新复制,每次都需要生成RDB文件并传输,费时耗资源,产生了增量复制。
增量复制
1、复制偏移量 replication offset
主服务和从服务器会分别维护一个复制偏移量,如果主从库复制偏移量相同,二者状态一致,如果偏移量不同,就说明是二者数据库状态不一致,从库缺少数据,需要使用增量复制来同步。
2、复制积压缓冲区 replication backlog
主库的写命令,除了传给从库后,还会写入replication backlog,这是一个固定长度的先进先出FIFO的队列,默认大小为1MB,其在内存是一个环形结构。
主库按照顺时针方向写入命令,主库最新写入位置即为上文提到的主库偏移量,这里的master offset,假设从库在set key2 2 后断开连接,也就是slave offset位置,当他重连时,再次给主库发送psync指令时,会带上offset。根据上下文,主库发现从库偏移量和主库不一致,需要进行增量备份,此时主库会计算出master offset和salve offset之间的指令,并发送给为该从库准备的replication buffer里,进而发送给从库,从库进行写入后便又恢复和主库一致的状态。
3、断开重连
断开重连不一定是增量复制。
- 整个replication backlog是个环形结构,也就是说最新的命令会将老的命令覆盖,断开时间太久,就只能进行全量复制了,所以可以将replication buffer配置尽量大一点。
- 每个值都有自己的runID,这个值在服务启动的时候自动生成,由40个随机16位字符组成,从库断开连接时,会将主库的runID一起发送过去,主库会判断runID是否为自己,就会告知进行全量复制。
8、哨兵
主要功能包括主节点存活检测、主从运行情况检测、自动故障转移、主从切换。redis sentinel最小配置是一主一从。他也是一台redis服务器,不对外提供服务,一般配置为单数。
Sentinel执行以下4个任务:
- 监控:不断检查主服务器和从服务器是否运行。
- 通知:哨兵检查服务器出现问题,就会通知其他哨兵。
- 自动故障转移:当主节点不能正常工作,Sentinel或开始一次主动的故障转移操作,将与失效主节点是主从关系的其中一个节点升级为新的主节点,并将其他从节点指向新的主节点,避免人工干预。
- 配置提供者:客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点信息。
监控的工作流程
①哨兵发送 info 指令,并且保存所有哨兵状态、主节点和从节点的信息。
②主节点会记录 redis 实例的信息,主节点记录的信息跟哨兵记录的信息看起来是一样的,实际上还是有点区别。
③哨兵会根据在主节点拿到的从节点信息,给对应的从节点也发送 info 指令。
④接着哨兵 2 来了,同样的也会给主节点发送 info 指令,并且建立 cmd 连接。
⑤这个时候哨兵 2 也会保存跟哨兵 1 一样的信息,只不过是保存的哨兵信息是 2 个。
⑥这个时候为了每个哨兵的信息都一致它们之间建立了一个发布订阅。为了哨兵之间的信息长期对称它们之间也会互发 ping 命令。
⑦当再来一个哨兵 3 时,也会做同样的事情,给主节点和从节点发送 info。并且跟哨兵 1 和哨兵 2 建立连接。
通知的工作流程
sentinel 会给主从的所有节点发送命令获取其状态,并且会把信息发布到哨兵的订阅里。
故障转移原理
哨兵会一直给主节点发送 publish sentinel:hello,直到哨兵报出 sdown。哨兵还会往内网里发布消息说明这个主节点挂了。其余的哨兵也会发送信息确认主节点是否真的挂了。都认为挂了,就会修改状态为odown。这时候哨兵们就会竞选谁去负责推选新节点。推选出来了就会断开原主节点,且其他节点连接新节点。
推选新主节点规则:
- 去掉不在线的
- 去掉响应慢的
- 与原主节点断开时间最久的干掉
- 根据优先级
- 判断offset偏移量,判断数据同步性
- 判断runid,时间早的上位
sdown和odown 主观宕机、客观宕机
quorum和majority
quorum:确认odown的最少的哨兵数量
majority:授权进行主从切换的最少的哨兵数量
哨兵的原理
- 第一个定时任务:每个Sentinel每隔1秒向master、slave以及其他sentinel发送ping命令。作用:作为心跳检测。
- 第二个定时任务:每个Sentinel每隔2秒向master的一个channel发送自己掌握的集群信息和自己的信息,利用redis的发布订阅功能,每个sentinel节点都会订阅这个channel,每个sentinel都会知道别的sentinel掌握的集群信息。作用是用于信息交换,了解别的sentinel信息和他们对于主节点的判断。
- 第三个定时任务:每隔10秒都会想master和slave发送INFO信息。作用:掌握最新的集群拓扑结构。
集群脑裂
就是一个集群中出现2个master情况。
如何发生的:当集群中的master网络出现问题,和集群中的slave和sentinel通信出现问题,但是本身没有挂。这个时候sentinel连不上master,选择一个slave变成master,这个时候client还没有来得及切换,就会把数据写入原来的master中,一旦网络恢复,原来的master就会变成slave,从新的master上复制数据,那么client写入旧的master的数据就会丢失。
如何解决:配置项min-slavers-to-write 1和min-slavers-max-lag 10。这2个配置项组合在一起就是至少有一个slave和master数据同步延迟不超过10秒,如果所有slave都超过10秒了,那么master就会拒绝接收请求。如果发生脑裂,如果client向旧master写入数据,旧的master不能向别的slave同步数据,所以client最多只能写入10秒的数据。如果出现旧的master和slave都和sentinel无法通信了,就可以调整min-slaver-to-write的大小。
缓存穿透
就是查询不到数据。用户查询数据,发现redis内存数据库中没有,就会向持久化层的数据库查询,发现也没有,于是本次查询失败,当用户很多的时候,缓存没有命中,于是都去请求了持久层数据库,给持久层数据库造成很大压力,出现缓存穿透。
解决方案:
布隆过滤器:是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的压力。
缓存空对象:就是当一个请求过来,缓存中和数据库都不存在,第一次请求会跳过缓存,进入数据库查询,并且访问数据库后返回空,此时将空值也进行缓存。
缓存击穿
就是一个key很热门,在不停的扛住大并发,大并发集中对一个点进行访问,当这个key失效的瞬间,持续的大并发穿破缓存,直接到达持久化数据库,对数据库的访问压力瞬间增大。
解决方案:
- 设置热点数据永不过期
- 加互斥锁。在用于进行大并发访问的时候,在查询缓存和数据库的过程中加锁,只能第一个进来的请求进行执行,当第一个请求把数据放进缓存中,接下来的访问就会访问缓存,防止缓存击穿。
缓存雪崩
指在一瞬间,缓存集中过期失效,无数请求绕过缓存,直接请求数据库。
造成原因:
- redis宕机
- 大部分数据失效
解决方案:
- 搭建高可用集群
- 设置不同的过期时间,防止同一时间内大量key失效。