【面试复习】数据库 - redis(上)
1、redis的各个数据类型?应用场景有?
string应用场景:
- 单值缓存 set key value ; get key
- 对象缓存 set obj:1:field1 value1; set obj:1:field2 value2;
- 分布式锁 每一个线程在对数据库操作前都要 :setnx dbname:tablename:id true; 如果设置成功,则表示拿到锁,失败表示拿锁失败,继续拿锁;对数据库操作以后, del dbname:tablename:id;释放锁
- 计数器
- 登录态session的保存
- 分布式全局序列号
hash应用场景:
- 对象缓存 - 购物车 可以设计为 set user:购物车 商品id:商品数量
list应用场景:
- 栈 lpush lpop
- 队列 lpush rpop
- 消息队列 lpush
2、redis持久化
目前redis持久化方式分为:RDB,AOF,混合持久化
(1)RDB
快照方式持久化,redis会把当前内存中的数据,按照二进制形式存储为一个dump.rdb文件,恢复时直接将这个二进制文件导入到内存中。
RDB快照存储触发条件:
- 客户端执行命令 save 或者 bgsave; (不管RDB有没有开启)
- 根据配置文件 save m n 规则自动生成快照; (RDB必须开启)
- 主从复制时,从库全量同步主库数据时,主库会执行bgsave; (不管RDB有没有开启)
- 客户端执行数据库清空命令 FLUSHALL时,触发快照; (RDB必须开启)
- 客户端执行shutdown关闭redis时,触发快照; (RDB必须开启)
RDB持久化配置
1 save m n 2 #配置快照(rdb)促发规则,格式:save <seconds> <changes> 3 #save 900 1 900秒内至少有1个key被改变则做一次快照 4 #save 300 10 300秒内至少有300个key被改变则做一次快照 5 #save 60 10000 60秒内至少有10000个key被改变则做一次快照 6 #关闭该规则使用svae “” 7 8 dbfilename dump.rdb 9 #rdb持久化存储数据库文件名,默认为dump.rdb 10 11 stop-write-on-bgsave-error yes 12 #yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件。 13 14 rdbchecksum yes 15 #在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。 16 17 dir "/etc/redis" 18 #数据文件存放目录,rdb快照文件和aof文件都会存放至该目录,请确保有写权限 19 20 rdbcompression yes 21 #是否开启RDB文件压缩,该功能可以节约磁盘空间
(2)AOF持久化
AOF将redis内的所有操作命令,以追加的方式存放在 appendonly.aof 文件中,redis重启恢复时,将 appendonly.aof 文件 所有命令再重新执行一次。aof也支持重写,将多个命令合并,这样可以减少操作命令的次数,redis恢复时间也会缩短。
redis将每写一条命令,以redis通信协议添加到缓冲区aof_buf,这样可以减少磁盘io的次数。
aof同步到磁盘的三种策略:
-
- no: 不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,在linux操作系统中大约每30秒刷一次缓冲。这种情况下,缓冲区数据同步不可控,并且在大量的写操作下,aof_buf缓冲区会堆积会越来越严重,一旦redis出现故障,数据丢失严重。
- always: 表示每次有写操作都调用fsync方法强制内核将数据写入到aof文件。这种情况下由于每次写命令都写到了文件中, 虽然数据比较安全,但是因为每次写操作都会同步到AOF文件中,所以在性能上会有影响,同时由于频繁的IO操作,硬盘的使用寿命会降低。
- everysec: 数据将使用调用操作系统write写入文件,并使用fsync每秒一次从内核刷新到磁盘。 这是折中的方案,兼顾性能和数据安全,所以redis默认推荐使用该配置。
aof 重写触发条件:
-
- 手动触发: 客户端执行 bgrewriteaof
- 自动触发:设置redis.conf 两个配置
- auto-aof-rewrite-min-size: AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。
- auto-aof-rewrite-percentage:当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比,如100代表当前AOF文件是上次重写的两倍时候才重写。
(3)混合持久化
RDB与AOF相结合
混合持久化同样是通过bgrewriteaof 完成的,不同aof的重写,混合持久化在开启这个命令时,会将当前内存副本以RDB的形式写入aof中,然后再将重写缓冲区的增量命令以AOF方式写入文件中
开启混合持久化时,aof文件可能的格式:
- aof文件开头是rdb,后面是aof
- aof一直是aof格式
配置:aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的
3、redis架构模型
主从架构、哨兵架构、集群架构
(1)主从架构
特点:一主多从;从节点从主节点同步数据;主节点挂了,则需要手动切换主节点;
主从工作原理:全量复制,部分复制
主从架构从节点配置:
1、复制一份redis.conf文件 2、将相关配置修改为如下值: port 6380 pidfile /var/run/redis_6380.pid6 logfile "6380.log" dir /usr/local/redis‐5.0.3/data/6380 3、配置主从复制 replicaof 192.168.0.60 6379 # 从本机6379的redis实例复制数据 replica‐read‐only yes 4、启动从节点 redis‐server redis.conf 5、连接从节点 redis‐cli ‐p 6380 6、测试在6379实例上写数据,6380实例是否能及时同步新修改数据
主从架构的使用:与使用单机redis一样,链接主节点,读写操作
(2)高可用哨兵架构
哨兵不提供服务,只用来监视服务,客户端第一次连接到哨兵服务,哨兵给到主节点的ip:port,之后客户端直接链接主节点读写操作;当主节点挂掉了,哨兵负责推举出新的主节点,并且发消息推送给客户端。
哨兵的配置,哨兵的代码接入,哨兵的选举,哨兵的优缺点。
哨兵的配置:
1、复制一份sentinel.conf文件 cp sentinel.conf sentinel‐26379.conf 2、将相关配置修改为如下值: port 26379 daemonize yes pidfile "/var/run/redis‐sentinel‐26379.pid" logfile "26379.log" dir "/usr/local/redis‐5.0.3/data" # sentinel monitor <master‐name> <ip> <redis‐port> <quorum> # quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 +1),master才算真正失效12 sentinel monitor mymaster 192.168.0.60 6379 2 3、启动sentinel哨兵实例 src/redis‐sentinel sentinel‐26379.conf 4、查看sentinel的info信息 src/redis‐cli ‐p 26379 127.0.0.1:26379>info 可以看到Sentinel的info里已经识别出了redis的主从 5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改
哨兵优点:相较于主从,主节点挂掉以后,哨兵可以自动选举新的主节点来代替原来的主节点,省去了主从架构的人工替换的过程;缺点:选举新的主节点,替换过程中,整个redis不可用;不易扩展,无法应对高并发场景;
(3)高可用集群架构
redis集群架构由多个redis主从节点群组构成。且不需要哨兵也能完成节点移除和故障转移,需要将每个节点设置成集群模式,这种集群模式没有中心节点,可以水平扩展。
每个来存储的key,都通过hash算法,计算出对应的hash值(在1~16384范围内);每一个加入集群的主从redis都会分配到一段对应的hash槽(同样只有主读写,从节点同步主节点数据)
redis集群的搭建,集群的扩容,缩容
redis主节点挂掉以后的内部选举,redis内部所有节点之间的互连,已经可能有的信号传输
1、redis集群配置过程
redis集群需要至少要三个master节点,我们这里搭建三个master节点,并且给每个master再搭建一个slave节点,总共6个redis节点,这里用三台机器部署6个redis实例,每台机器一主一从,搭建集群的步骤如下:
第一步:在第一台机器的/usr/local下创建文件夹redis‐cluster,然后在其下面分别创建2个文件夾如下 (1)mkdir ‐p /usr/local/redis‐cluster (2)mkdir 8001 8004 第一步:把之前的redis.conf配置文件copy到8001下,修改如下内容: (1)daemonize yes (2)port 8001(分别对每个机器的端口号进行设置) (3)dir /usr/local/redis‐cluster/8001/(指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据) (4)cluster‐enabled yes(启动集群模式) (5)cluster‐config‐file nodes‐8001.conf(集群节点信息文件,这里800x最好和port对应上) (6)cluster‐node‐timeout 5000 (7)# bind 127.0.0.1(去掉bind绑定访问ip信息) (8)protected‐mode no (关闭保护模式) (9)appendonly yes 如果要设置密码需要增加如下配置: (10)requirepass zhuge (设置redis访问密码) (11)masterauth zhuge (设置集群节点间访问密码,跟上面一致) 第三步:把修改后的配置文件,copy到8004,修改第2、3、5项里的端口号,可以用批量替换:20 :%s/源字符串/目的字符串/g 第四步:另外两台机器也需要做上面几步操作,第二台机器用8002和8005,第三台机器用8003和8006 第五步:分别启动6个redis实例,然后检查是否启动成功 (1)/usr/local/redis‐5.0.3/src/redis‐server /usr/local/redis‐cluster/800*/redis.conf (2)ps ‐ef | grep redis 查看是否启动成功 第六步:用redis‐cli创建整个redis集群(redis5以前的版本集群是依靠ruby脚本redis‐trib.rb实现) # 下面命令里的1代表为每个创建的主服务器节点创建一个从服务器节点 # 执行这条命令需要确认三台机器之间的redis实例要能相互访问,可以先简单把所有机器防火墙关掉,如果不关闭防火墙则需要打开redis服务端口和集群节点gossip通信端口 # 关闭防火墙 # systemctl stop firewalld # 临时关闭防火墙 # systemctl disable firewalld # 禁止开机启动 (1)/usr/local/redis‐5.0.3/src/redis‐cli ‐a zhuge ‐‐cluster create ‐‐cluster‐replicas 1 192.168.0.61:8001 192.168.0.62:8002 192.168.0.63:8003 192.168.0.61:8004 192.168.0.62:8005 192.168.0.63:8006 第七步:验证集群: (1)连接任意一个客户端即可:./redis‐cli ‐c ‐h ‐p (‐a访问服务端密码,‐c表示集群模式,指定ip地址和端口号)如:/usr/local/redis‐5.0.3/src/redis‐cli ‐a zhuge ‐c ‐h 192.168.0.61 ‐p 800* (2)进行验证: cluster info(查看集群信息)、cluster nodes(查看节点列表) (3)进行数据操作验证 (4)关闭集群则需要逐个进行关闭,使用命令: /usr/local/redis‐5.0.3/src/redis‐cli ‐a zhuge ‐c ‐h 192.168.0.60 ‐p 800* shutdown
第八步:redis集群的命令帮助
redis-cli --cluster help
create: 创建一个集群环境 host1:port1 ... hostN:portN
call: 可以执行redis命令
add-node: 添加节点进入集群,扩容
del-node: 移除节点,缩容
reshard: 重写分片
check: 检查集群状态
扩容:
1、redis-cli -a password --cluster add-node 192.168.0.61:8009 192.168.0.61:8010
2、新增加的节点都是主节点,且没有被分配任何slot(hash槽位)
3、找到集群中任意一个已经有槽位的主节点: redis-cli -a password --cluster reshard 192.168.0.61:8001
输出如下:
... ...
How many slots do you want to move(from 1 to 16384)? 600
(需要多少个槽位移动到新的节点上,这边设置的600个槽位)
What is the receiving node ID? 2728...
(把这600个槽位移动到哪个节点上去,指定节点id,节点id可以通过 cluster node查看)
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node 1: all
(输入all为 从所有主节点中分别抽取槽数到指定的新节点中去,抽取的总数为600)
4、增加从节点: 连接到需要被添加的从节点上去, redis-cli -a password -c -h 192.168.0.61 -p 8010
设置为从节点: 192.168.0.61:8010> cluster replicate 2728... #2728... 为主节点的id
缩容:
1、删除从节点: redis-cli -a password --cluster del-node 192.168.0.61:8010 a1cfe... #a1cfe... 是8010的节点id
2、删除主节点: 先把主节点的槽位也去掉
redis-cli -a password --cluster reshard 192.168.0.61:8009
输出如下:
... ...
How many slots do you want to move(from 1 to 16384)? 600
What is the receiving node ID? dfca...
Please enter all the source node IDs.
Type 'all' ...
Type 'done' ...
Source node 1:2728...
(这里输出数据源为8009节点id)
Source node 2:done
然后再删除节点
2、redis集群原理
redis将每个来存储的key值通过hash算法,计算出对应的hash值(在1~16384范围内);每个节点负责分配其中的一部分槽位。当redis客户端来连接集群时,他会得到一份集群的槽位配置信息并 将其缓存到客户端,这样客户端要查找某个key时,可以直接定位到目标节点,同时因为槽位信息可能会存在服务端与客户端不一致的情况,还需要纠正机制来实现槽位信息的校验调整。
2.1、槽位定位算法: HASH_SLOT = CRC16(key) mod 16384
ps:关于为什么redis总的槽位数目被设置成 16384 的疑惑,可参考 https://www.cnblogs.com/rjzheng/p/11430592.html 总结一下就是:
-
-
- redis各个实例之间会经常性的与其他实例通信(发ping消息);
- ping消息内包含了这个节点所占用槽位的bitmap信息;
- 如果槽位过多,会导致bitmap过大,会导致发送的ping消息过大,占用了多大的网络带宽;
- 16384对于1000以内的节点数的集群来说已经足够了,这样我们在设置集群数目的时候,不要考虑1000以上的节点数。
-
2.2、跳转重定向&槽位纠正机制
当一个客户端向一个错误的节点发出指令,该节点发现指令key的槽位不归自己管理,这时他会向客户端发一个特殊的跳转指令携带目标操作的节点地址,客户端在跳转的同时还会同步的纠正 本地的槽位缓存映射表。
2.3、redis集群间的通信协议
集群间通信方式分为 集中式和gossip;redis采用了gossip方式。gossip协议端口为:自己服务的端口号+10000
- ping:每个节点都会频繁的给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据。
- pong:用以返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新。
- fail:某个节点判断另一个节点fail以后,就发送fail给其他节点
- meet:某个节点发送meet给新加入的节点,让新节点加入集群,然后新节点就会开始与其他节点通信
2.4、redis集群选举原理
当从节点发现无法链接主节点(并且持续了cluster-node-timeout 时间)就认定了该主节点已经fail了;
从节点将自己记录的集群currentEpoch 加1,并广播 FAILOVER_AUTH_REQUEST 信息;所有接收到信息的节点中,只有其他主节点 可以响应,判断请求者的合法性,并发送 FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack
所有发送 FAILOVER_AUTH_REQUEST 的从节点,收集 FAILOVER_AUTH_ACK的回包信息。如果有一方收到超过半数master的ack后,则变成新的master(这也解释了为什么集群最少 需要三个主节点,如果只有两个,当其中一个挂了,只剩一个主节点是不能选举成功的 )
2.5、集群为什么至少需要三个节点,且推荐节点数为奇数
节点数选择为奇数主要是从节约机器的角度触发的,节点数三个或者四个,都满足挂掉一个主节点可以选举,挂掉两个主节点无法选举的情况。