近况
最近两天把redis复习了一下,确实学习到了很多新的东西,我对redis的了解还远远不够啊,慢慢学吧,
之前有搭过redis主从复制,哨兵模式,分片集群,现在使用docker重新搭建了一次,当然复习redis不光是搭建
这些东西,主要目标是想学习redis的分布式缓存,认识一下到底什么是分布式缓存,OK,回归正题,
现在暂且记录一下docker搭建三个基本的redis模型吧。
Docker搭建主从复制
基本配置文件和目录
redis-MasterAndSlave/ #主目录 ├── docker-compose.yaml ├── master/ #目录 │ ├── data/ #目录 │ ├── redis.conf ├── slave1/ │ ├── data/ │ ├── redis.conf └── slave2/ ├── data/ ├── redis.conf
redis.conf
master中redis.confport 6379 protected-mode no repl-diskless-sync no repl-disable-tcp-nodelay no requirepass master123765 #给主机设置密码
slave中redis.conf
port 6379 protected-mode no slaveof 172.25.0.101 6379 masterauth master123765 #主机密码 slave-read-only yes slave-serve-stale-data yes requirepass 123765 #从机密码
docker-compose.yaml
version: '3' services: #reids-master redis-master: container_name: redis-master image: redis:latest ports: - '6379:6379' networks: mynetwork: #配置固定IP ipv4_address: 172.19.0.10 volumes: - "./master/redis.conf:/etc/redis.conf" - "./master/data:/data" #redis的持久化配置文件将会保存在该目录 command: ["redis-server", "/etc/redis.conf"] restart: always #容器启动失败后会不断尝试重启 #reids-slave1 redis-slave1: container_name: redis-slave1 image: redis:latest depends_on: - redis-master ports: - '6380:6379' networks: mynetwork: #配置固定IP ipv4_address: 172.19.0.11 volumes: - "./slave1/redis.conf:/etc/redis.conf" - "./slave1/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always #reids-slave2 redis-slave2: container_name: redis-slave2 image: redis:latest depends_on: - redis-master ports: - '6381:6379' networks: mynetwork: #配置固定IP ipv4_address: 172.19.0.12 volumes: - "./slave2/redis.conf:/etc/redis.conf" - "./slave2/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always networks: mynetwork: #配置一个子网,前提是没有这个网络,如果有,docker network rm mynetwork driver: bridge #这个子网会在配置文件启动时自动创建 ipam: config: - subnet: 172.19.0.0/24
然后启动主从复制
在主目录下执行,docker-compose up -d
,关闭直接:docker-compose down
进入master容器中:docker exec -it redis-master bash
进入redis客户端:redis-cli -a master123765
,需填写密码
或者你可以先进入客户端:redis-cli
,然后再密码认证auth master123765
注意:主从复制模式下,主机挂掉后,从机并不会上位,依然是从机状态,当主机重新上线后,
仍然维持主机状态,若想当主机挂掉后,从机上位成主机,除了手动通过命令外,还可使用哨兵模式。
Docker搭建哨兵模式
哨兵,用来监控master节点,当master节点挂掉后,通过选举将slave节点转换成master结点,
哨兵节点务必大于三个才有效,哨兵节点需要获取所有节点的信息,但其只需监控master即可,通过master
即可获取到slave节点,其他哨兵结点的信息,下面是搭建步骤基本配置文件和目录
sentinel/ ├── docker-compose.yml ├── master/ │ ├── data/ │ └── redis.conf ├── slave1/ │ ├── data/ │ └── redis.conf ├── slave2/ | ├── data/ | └── redis.conf ├── sentinel1 │ └── sentinel.conf ├── sentinel2 │ └── sentinel.conf └── sentinel3 └── sentinel.conf
redis.conf
master的redis.confport 6379 pidfile /var/run/redis_6379.pid protected-mode no timeout 0 tcp-keepalive 300 loglevel notice requirepass 123765 ################################# REPLICATION ################################# slave-serve-stale-data yes slave-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no ##################################### RDB ##################################### dbfilename dump.rdb save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dir ./ ##################################### AOF ##################################### appendonly yes appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no aof-load-truncated yes aof-use-rdb-preamble no
slave的redis.conf
# 在master的redis.conf的REPLICATION下添加,其他与master一致 slaveof 172.19.0.10 6379 #主机地址 masterauth 123765 #主机的密码 requirepass 123765 #自己redis的密码 注意:哨兵模式下,主机的密码和从机的密码必须一样,不然从机上位不了!!!
sentinel.conf
# 所有哨兵端口都一致,因为使用 Docker 桥接网络映射 port 26379 # 哨兵设置,所有哨兵皆一致,都指向 Master sentinel monitor mymaster 172.19.0.10 6379 2 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster 123765 #配置主机密码,从机和主机密码务必一致 sentinel down-after-milliseconds mymaster 30000 sentinel failover-timeout mymaster 180000 bind 0.0.0.0 protected-mode no daemonize no pidfile /var/run/redis-sentinel.pid logfile "" dir /tmp
docker-compose.yaml
version: "3" networks: redis-replication: driver: bridge ipam: config: - subnet: 172.19.0.0/24 services: master: image: redis container_name: redis-master ports: - "6379:6379" volumes: - "./master/redis.conf:/etc/redis.conf" - "./master/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.10 slave1: image: redis container_name: redis-slave-1 ports: - "6380:6379" volumes: - "./slave1/redis.conf:/etc/redis.conf" - "./slave1/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.11 slave2: image: redis container_name: redis-slave-2 ports: - "6381:6379" volumes: - "./slave2/redis.conf:/etc/redis.conf" - "./slave2/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.12 sentinel1: image: redis container_name: redis-sentinel-1 ports: - "26380:26379" volumes: - "./sentinel1/sentinel.conf:/etc/sentinel.conf" command: ["/bin/bash", "-c", "cp /etc/sentinel.conf /sentinel.conf && redis-sentinel /sentinel.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.13 sentinel2: image: redis container_name: redis-sentinel-2 ports: - "26381:26379" volumes: - "./sentinel2/sentinel.conf:/etc/sentinel.conf" command: ["/bin/bash", "-c", "cp /etc/sentinel.conf /sentinel.conf && redis-sentinel /sentinel.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.14 sentinel3: image: redis container_name: redis-sentinel-3 ports: - "26382:26379" volumes: - "./sentinel3/sentinel.conf:/etc/sentinel.conf" command: ["/bin/bash", "-c", "cp /etc/sentinel.conf /sentinel.conf && redis-sentinel /sentinel.conf"] restart: always networks: redis-replication: ipv4_address: 172.19.0.15
以上就是docker搭建哨兵模式的配置步骤
Docker搭建分片集群
主从复制解决了 Redis 的性能问题,哨兵模式解决了 Redis 的可用性问题,但单个master仍然抗住高并发写
因此分片集群产生,分片集群有多个master,每个master至少一台slave,分片指的是将数据分片存储。
分片集群会根据数据计算分出16384个插槽(0-16383),插槽指的是存储单位,任何一个数据要想存储到集群
都会将根据该数据做一个计算,看这个数据属于哪个插槽,而不同的master管理不同范围的插槽。
如果一个客户端向集群中一个结点存储数据,redis便通过计算,判断这个数据属于哪个插槽,再将请求转给
相应的master节点进行处理,所以不管你向集群中master结点存储数据,还是slave结点存储数据都行,
redis会帮助你转发请求。这个分片就是划分不同的master管理不同范围的插槽,通过计算划分16384个插槽
再根据数据计算该数据应该存放在哪个插槽中,这不就是散列存储,像散列表一样,大概如此吧。分片集群最少需要6个结点,3主3从,下面是分片集群的配置步骤
基本的配置文件和目录
redis-cluster/ ├── docker-compose.yaml ├── node1 │ ├── data │ └── redis.conf ├── node2 │ ├── data │ └── redis.conf ├── node3 │ ├── data │ └── redis.conf ├── node4 │ ├── data │ └── redis.conf ├── node5 │ ├── data │ └── redis.conf └── node6 ├── data └── redis.conf
redis.conf
port 6371 #6371~6376 protected-mode no daemonize no ################################ REDIS CLUSTER ############################### cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 15000 cluster-announce-bus-port 16371 #16371~16376
6个redis.conf,除了port和cluster-announce-bus-port不同,其他都一样。
docker-compose.yaml
version: "3" services: node1: image: redis container_name: redis-cluster-node-1 network_mode: "host" volumes: - "./node1/redis.conf:/etc/redis.conf" - "./node1/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always node2: image: redis container_name: redis-cluster-node-2 network_mode: "host" volumes: - "./node2/redis.conf:/etc/redis.conf" - "./node2/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always node3: image: redis container_name: redis-cluster-node-3 network_mode: "host" volumes: - "./node3/redis.conf:/etc/redis.conf" - "./node3/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always node4: image: redis container_name: redis-cluster-node-4 network_mode: "host" volumes: - "./node4/redis.conf:/etc/redis.conf" - "./node4/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always node5: image: redis container_name: redis-cluster-node-5 network_mode: "host" volumes: - "./node5/redis.conf:/etc/redis.conf" - "./node5/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always node6: image: redis container_name: redis-cluster-node-6 network_mode: "host" volumes: - "./node6/redis.conf:/etc/redis.conf" - "./node6/data:/data" command: ["redis-server", "/etc/redis.conf"] restart: always
这6个节点都是部署在宿主机上的,所以他们的端口号不同,之前sentinel,主从复制都是配置在子网中
redis集群的结点如果配置在子网中,那么当宿主机使用客户端存储数据时,redis可能将请求转发到其他
结点做存储,但是这些结点都是子网ip,宿主机根本访问不到,会造成阻塞,所以分片集群最好部署在宿主机上。
Redis的主从复制原理
- 从机会发消息给主机,要求增量同步,此时主机会检查从机的replication id和offset数据偏移量
每个redis服务都会有一个id,称为replication id,如果两个redis进程replId一致,说明是主从关系
offset则是该redis进程从其他redis进程拷贝的数据的偏移量。 - 主机接收到从机发过来的消息,它需要判断,如果从机是第一次发送请求,则需要拒绝该增量同步请求
需要全量同步,而如果不是第一次发送请求,则同意它的增量请求,判断从机是否是第一次请求,
需要根据replId来判断,redis中规定,每个redis结点最初replId都不一致,如果称为了主从关系,
则从机的replId需要与主机的replId保持一致,比较两者的replId就能知道是不是第一次发送请求了。 - 如果不是第一次发送请求,则说明之前已经发送过数据给从机了,此时没必要再全量同步,而是同意增量
请求,如果是第一次发送请求,则拒绝增量请求,需要全量请求,现在假设该从机是第一次发送消息
此时主机会有两个操作
1,主机会生成一个线程去拷贝内存中的数据进行持久化成rdb文件,拷贝好rdb文件后,将该文件发给从机
2,在线程持久化内存数据的时候,主机会开辟一个内存缓存区,叫repl_baklog
的区域,用来存储在此
期间接收的客户端请求的数据。 - 当从机接收到rdb文件后,会清除本地自己的rdb文件,加载接收到的rdb文件,从而获得主机的数据
从机拿到主机的数据,并不是完整的,因为可能在主机持久化数据的时候,有新的数据,被存到内存缓存区中
所以从机再次发送增量请求 - 主机仍然判断从机是否为第一次发送消息,发现replId一致,则同意增量请求,将repl_baklog中的数据
发送给从机,但并不是将内存缓冲区数据全部发送给从机,而是根据从机的offset来确定到底发多少数据
内存缓冲区相当于一个双向链表,或者说是一个环,从机的offset和主机的offset像两个指针一样,
它们之间的间隔就是主机需要发送的数据,所以我们可以知道,从机的offset一定是小于主机的offset的
如果它们的offset则说明它们的数据一致,完成同步。
- 从机会发消息给主机,要求增量同步,此时主机会检查从机的replication id和offset数据偏移量
文章与资料
掘金有非常多好文章,我也是看着别人的文章搭建的,下面放上大佬的文章和一些网课资料
资料中有上述的案例文件,中文解析的redis.conf,redis数据同步原理的ppt