9. Docker 当中的复杂安装(MySQL主从复制的安装,Redis 的3主3从的安装配置)的详细说明[附加步骤图]
文章目录
- 9. Docker 当中的复杂安装(MySQL主从复制的安装,Redis 的3主3从的安装配置)的详细说明[附加步骤图]
- 1. 在 Docker 容器当中安装 MySQL并实现 主从复制
- 2. Docker 当中安装 redis 集群(大厂面试题第4季-分布式存储案例真题)
- 面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例 ?
- 2.1.1 哈希取余分区
- 2.1.2 一致性哈希算法分区
- 2.1.3 哈希槽分区
- 3. Docker 当中配置 Redis 3主3从 redis 集群扩缩容配置
- 3.1 Redis 3 主 3 从 redis 集群配置
- 3.2 Redis 容器实例 主从容错切换迁移
- 3.3 Redis 容器实例 主从扩容(就是增加一个新的(1主1从)现在的 3 主 3 从,变为 4 主 4 从)
- 3.4 Redis 主从缩容{就是从新增的( 4 主 4 从),缩小为原来的(3 主 3 从)案例
- 4. 最后:
1. 在 Docker 容器当中安装 MySQL并实现 主从复制
关于: MySQL 的主从复制的原理,这里就不再赘述了。这里我们主要说明的是在 Docker 当中配置实现 MySQL 的主从复制。
- 新建
"主"
服务器 MySQL容器实例 3307
注意:对应 MySQL 版本镜像的 pull 拉取。
如下的命令是创建了对应 MySQL 容器实例的同时,配置了容器数据卷 。
docker run -p 3307:3306 --name mysql-master01 \
-v /mydata/mysql-master/log:/var/log/mysql \
-v /mydata/mysql-master/data:/var/lib/mysql \
-v /mydata/mysql-master/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
- 主机:进入
/mydata/mysql-master/conf
这是MySQL “主” 服务容器数据卷目录下新建 my.cnf
因为我们在该路径下配置了容器数据卷 的路径,当该 MySQL 容器实例启动的时候,会读取到该容器数据卷的数据,进行配置处理。
[root@localhost ~]# cd /mydata/mysql-master/conf
[root@localhost conf]# vim my.cnf
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=101
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能
log-bin=mall-mysql-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
- 修改完配置后重启 mysql-master01 容器实例
[root@localhost ~]# docker restart mysql-master01
- 进入mysql-master01容器实例当中:
[root@localhost ~]# docker exec -it mysql-master01 /bin/bash
bash-4.2# mysql -uroot -p
- mysql-master01 容器实例内创建数据同步用户 。
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
- 新建
"从"
服务器 MySQL 容器实例 3308
docker run -p 3308:3306 --name mysql-slave01 \
-v /mydata/mysql-slave/log:/var/log/mysql \
-v /mydata/mysql-slave/data:/var/lib/mysql \
-v /mydata/mysql-slave/conf:/etc/mysql/conf.d \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
- 进入
/mydata/mysql-slave/conf
这是 MySQL “从” 服务容器实例,容器数据卷目录下新建 my.cnf
[root@localhost conf]# cd /mydata/mysql-slave/conf
[root@localhost conf]# pwd
/mydata/mysql-slave/conf
[root@localhost conf]# ll
total 0
[root@localhost conf]# vim my.cnf
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
- 修改完配置后重启 mysql-slave01 “从” 服务容器实例,读取我们在容器数据卷当中共的配置,重启才会生效 。
[root@localhost conf]# docker restart mysql-slave01
- 进入到“主服务” MySQL 容器实例的数据库中查看主从同步状态
mysql> show master status;
10 . 进入 mysql-slave01 “从” MySQL 容器实例当中:
[root@localhost conf]# docker exec -it mysql-slave01 /bin/bash
bash-4.2# mysql -uroot -p
Enter password:
- 进入在“从” MySQL 数据库容器实例当中配置主从复制:
change master to master_host='宿主机ip(ifconfig 查询到的)', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
主从复制命令参数说明:
master_host:主数据库的IP地址;
master_port:主数据库的运行端口;
master_user:在主数据库创建的用于同步数据的用户账号;
master_password:在主数据库创建的用于同步数据的用户密码;
master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
master_connect_retry:连接失败重试的时间间隔,单位为秒。
change master to master_host='192.168.76.148', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;
- 进入到 “从”MySQL数据库实例容器当中查看主从同步状态
mysql> show slave status \G;
- 进入 “从” MySQL数据库容器实例当中开启主从同步。
start slave; # 表示对该 slave 用户开启 主从复制
- 进入到 “从” MySQL 数据库容器实例当中,再次执行
show slave status \G;
查看状态发现已经同步
mysql> show slave status \G;
- 主 从 复制测试:
- 进入在“主” MySQL 容器实例当中新建库-使用库-新建表-插入数据,ok:
mysql> create database db01; Query OK, 1 row affected (0.00 sec) mysql> use db01; Database changed mysql> create table t1(id int,name varchar(255)); Query OK, 0 rows affected (0.02 sec)mysql> insert into t1 values(1,"lihua"); Query OK, 1 row affected (0.06 sec)mysql> select * from t1; +------+-------+ | id | name | +------+-------+ | 1 | lihua | +------+-------+ 1 row in set (0.00 sec)
- 进入到 “从” MySQL 容器实例当中-使用库-查看记录,ok
mysql> use db01; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -ADatabase changed mysql> select * from t1; +------+-------+ | id | name | +------+-------+ | 1 | lihua | +------+-------+ 1 row in set (0.00 sec)
2. Docker 当中安装 redis 集群(大厂面试题第4季-分布式存储案例真题)
cluster(集群)模式-docker版 哈希槽分区进行亿级数据存储。
面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例 ?
单机单台100%不可能,肯定是分布式存储,用redis如何落地?
上述问题阿里P6~P7工程案例和场景设计类必考题目,一般业界有3种解决方案
- 哈希取余分区
- 一致性哈希算法分区
- 哈希槽分区
2.1.1 哈希取余分区
缺点:
原来规划好的节点,进行扩容或者缩容就比较麻烦了额,不管扩缩,每次数据变动导致节点有变动,映射关系需要重新进行计算,在服务器个数固定不变时没有问题,
如果需要弹性扩容或故障停机的情况下,原来的取模公式就会发生变化:Hash(key) / 3会变成Hash(key) / ? 。
此时地址经过取余运算的结果将发生很大变化,根据公式获取的服务器也会变得不可控。
某个redis机器宕机了,由于台数数量变化,会导致hash取余全部数据重新洗牌。
2.1.2 一致性哈希算法分区
一致性Hash 算法背景:
一致性哈希算法在 1997 年由麻省理工学院中提出的,设计目标是为了解决**分布式缓存数据变动和映射问题
,某个机器宕机了,分母数量改变了,自然取余数就不行了。
提出一致性Hash解决方案目的是:当服务器个数发生变动时,尽量减少影响客户端到服务器的映射关系。
3大步骤:
算法构建一致性哈希环:
一致性哈希环: 一致性哈希算法必然有个 hash 函数并按照算法产生 hash 值,这个算法的所有可能哈希值会构成一个全量集,这个集合可以成为一个 hash 空间[0,2^32-1],这个是一个线性空间,但是在算法中,我们通过适当的逻辑控制将它首尾相连(0 = 2^32),这样让它逻辑上形成了一个环形空间。
它也是按照使用取模的方法,前面笔记介绍的节点取模法是对节点(服务器)的数量进行取模。
而一致性 Hash 算法是对 2 ^ 32 取模,简单来说,一致性 Hash 算法将整个哈希值空间组织成一个虚拟的圆环。 如假设某哈希函数 H 的值空间为 0 - 2^32-1(即哈希值是一个 32 位无符号整形),整个哈希环如下图:整个空间 按顺时针方向组织 ,圆环的正上方的点代表0,0 点右侧的第一个点代表1,以此类推,2,3,4,…直到 2^32 - 1,也就是说 0 点左侧的第一个点代表 2 ^ 32 -1,0 和 2 ^32 - 1 在零点中方向重合,我们把这个由 2 ^32 个点组成的圆环称为 Hash 环。
- 服务器IP节点映射
将集群中各个 IP节点映射到环上的某一个位置。
将各个服务器使用 Hash 进行一个哈希,具体可以选择服务器的 IP 或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。假如 4 个节点 Node A,B,C,D,经过 IP 地址的哈希函数 计算(hash(ip)) ,使用 IP 地址哈希值后在环空间的位置如下:
- key 落到服务器的落键规则
当我们需要存储一个 kv 键值对时,首先计算 key 的 hash 值,hash(key) 将这个 key 使用相同的函数 Hash 计算出哈希值并确定此数据在环上的位置。从此位置沿顺时针“行走” ,第一台遇到的服务器就是其应该定位的服务器,并将该键值对存储在该节点上。
如我们有 ObjectA,ObjectB,Object C ,Object D 四个数据对象,经过哈希计算后,在环空间上的位置如下:根据一致性 Hash 算法,数据 A 会被定位到 Node A上,B 被定位到 Node B 上,C 被定位到 Node C 上,D 被定为到 Node D 上。
一致性哈希算法分区的优点:
- 一致性哈希算法的容错性
假设Node C宕机,可以看到此时对象A、B、D不会受到影响,只有C对象被重定位到Node D。一般的,在一致性Hash算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据,其它不会受到影响。简单说,就是C挂了,受到影响的只是B、C之间的数据,并且这些数据会转移到D进行存储。、
- 一致性哈希算法的扩展性
数据量增加了,需要增加一台节点NodeX,X的位置在A和B之间,那收到影响的也就是A到X之间的数据,重新把A到X的数据录入到X上即可,不会导致hash取余全部数据重新洗牌。
**一致性哈希算法分区的缺点:**一致性哈希算法的数据倾斜问题
Hash环的数据倾斜问题
一致性Hash算法在服务节点太少时,容易因为节点分布不均匀而造成数据倾斜(被缓存的对象大部分集中缓存在某一台服务器上)问题, 例如系统中只有两台服务器:
小总结:
- 为了在节点数目发生改变时尽可能少的迁移数据
将所有的存储节点排列在收尾相接的Hash环上,每个key在计算Hash后会顺时针找到临近的存储节点存放。
而当有节点加入或退出时仅影响该节点在Hash环上顺时针相邻的后续节点。
优点
加入和删除节点只影响哈希环中顺时针方向的相邻的节点,对其他节点无影响。缺点
数据的分布和节点的位置有关,因为这些节点不是均匀的分布在哈希环上的,所以数据在进行存储时达不到均匀分布的效果。
2.1.3 哈希槽分区
哈希槽实质就是一个数组,数组 [0,2^14 -1]
形成 hash slot 空间。
哈希槽解决均匀分配的问题,在数据和节点之间又加入了一层,把这层称为哈希槽(slot),用于管理数据和节点之间的关系,现在就相当于节点上放的是槽,槽里放的是数据。
槽解决的是粒度问题,相当于把粒度变大了,这样便于数据移动。
哈希解决的是映射问题,使用key的哈希值来计算所在的槽,便于数据分配
多少个 hash 槽
一个集群只能有16384个槽,编号0-16383(0-2^14-1)。这些槽会分配给集群中的所有主节点,分配策略没有要求。可以指定哪些编号的槽分配给哪个主节点。集群会记录节点和槽的对应关系。解决了节点和槽的关系后,接下来就需要对key求哈希值,然后对16384取余,余数是几key就落入对应的槽里。slot = CRC16(key) % 16384。以槽为单位移动数据,因为槽的数目是固定的,处理起来比较容易,这样数据移动问题就解决了。
哈希槽的计算:
Redis 集群中内置了 16384 个哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,也就是映射到某个节点上。如下代码,key之A 、B在Node2, key之C落在Node3上
3. Docker 当中配置 Redis 3主3从 redis 集群扩缩容配置
3.1 Redis 3 主 3 从 redis 集群配置
-
关闭防火墙+启动 docker 后台服务
-
新建 6 个 docker 容器 redis 实例
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
- 进入容器 redis-node-1 并为6台机器构建集群关系
docker exec -it redis-node-1 /bin/bash
构建主从关系(在)
注意,进入 docker 容器后才能执行一下命令,且注意自己的真实 IP 地址。
redis-cli --cluster create 192.168.76.149:6381 192.168.76.149:6382 192.168.76.149:6383 192.168.76.149:6384 192.168.76.149:6385 192.168.76.149:6386 --cluster-replicas 1
- 链接进入6381 的 Redis 容器实例作为切入点,查看集群状态:
root@localhost:/data# redis-cli -p 6381
127.0.0.1:6381>
127.0.0.1:6381> cluster info
127.0.0.1:6381> cluster nodes
3.2 Redis 容器实例 主从容错切换迁移
- 数据读写存储:
对6381新增两个key
[root@localhost ~]# docker exec -it redis-node-1 /bin/bash
root@localhost:/data# redis-cli -c -p 6381
查看集群信息
redis-cli --cluster check IP地址:对应的Redis 的端口
redis-cli --cluster check 192.168.76.149:6381
- 容错切换迁移:
[root@localhost ~]# docker start redis-node-1
先还原之前的3主3从对应的关系:
[root@localhost ~]# docker start redis-node-1
redis-node-1
[root@localhost ~]# docker stop redis-node-6
redis-node-6
[root@localhost ~]# docker start redis-node-6
redis-node-6
[root@localhost ~]#
注意:每个指令稍微暂停一下,让 Redis 心跳机制,反应过来。
在 Redis 容器实例当中,查看集群状态:
redis-cli --cluster check 自己IP:6381
redis-cli --cluster check 192.168.76.149:6381
3.3 Redis 容器实例 主从扩容(就是增加一个新的(1主1从)现在的 3 主 3 从,变为 4 主 4 从)
- 新建6387、6388两个节点+新建后启动+查看是否8节点
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
docker ps
- 进入6387 Redis 容器实例内部
docker exec -it redis-node-7 /bin/bash
查看集群状态。
- 将新增的6387节点(空槽号)作为 master节点加入原集群
将新增的6387作为master节点加入集群
redis-cli --cluster add-node 自己实际IP地址:6387 自己实际IP地址:6381
6387 就是将要作为master新增节点
6381 就是"原来"集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
redis-cli --cluster add-node 192.168.76.149:6387 192.168.76.149:6381
- 检查集群情况
redis-cli --cluster check 真实ip地址:6381
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6381
- 重新分派槽号,各自 M 节点,云一点给新加入的 6387 节点 。
重新分派槽号,
命令:redis-cli --cluster reshard IP地址:端口号
(任意端口集群当中的那个“主”节点的端口都行,只是以这个端口作为,入口进入而已。)
redis-cli --cluster reshard 192.168.76.149:6381
How many slots do you want to move (from 1 to 16384)? 4096
您想移动多少个插槽(从1到16384)?
What is the receiving node ID? 049d3835148bb72950d87eaea955bbfe9f7132ad
接收节点ID是多少?
How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? 049d3835148bb72950d87eaea955bbfe9f7132ad
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,删除选择 done
Do you want to proceed with the proposed reshard plan (yes/no)? yes
- 检查集群情况第2次
redis-cli --cluster check 真实ip地址:端口
(任意端口集群当中的那个“主”节点的端口都行,只是以这个端口作为,入口进入而已。)
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6381
- 为主节点6387分配从节点6388
命令:redis-cli --cluster add-node ip:新slave端口 ip:新master端口 --cluster-slave --cluster-master-id 新"主master"机节点ID
redis-cli --cluster add-node 192.168.76.149:6388 192.168.76.149:6387 --cluster-slave --cluster-master-id 049d3835148bb72950d87eaea955bbfe9f7132ad -------这个是6387的编号,按照自己实际情况
- 检查集群情况第3次
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6381
root@localhost:/data# redis-cli -p 6387
127.0.0.1:6387> cluster nodes
注意:扩容,添加新的主从到集群当中时的步骤:
- 添加好新的 Redis 节点。
- 先将 “主” 节点添加到集群当中,同时分配槽位。
- 分配好槽位后,再将 “从” 节点添加到新增的 “主” 节点当中。
3.4 Redis 主从缩容{就是从新增的( 4 主 4 从),缩小为原来的(3 主 3 从)案例
这里我们将,之前新增的 6387 主 和 6388 这 1 主 1 从,给删除了,从当前集群当中移除。
注意:缩容(移除节点)的步骤:
- 不可以先移除 “主” 节点,而是先移除**“从”** 节点,因为主节点是可以写入数据的,而从节点是存储数据的,先移植数据
- 再将要移除的 “主”节点的 槽位移植到其他的节点当中。
- 检查集群情况1获得 6388 "从节点"的节点ID
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6382
acc8f1d511c5bcc5a0f6f9c98e3434196dc10257
- 进入到 Redis 容器实例当中,将 6388 “从”节点删除,从集群中移除 6388 “从” 节点:
命令:redis-cli --cluster del-node ip:从机端口 从机6388节点ID
redis-cli --cluster del-node 192.168.76.149:6388 acc8f1d511c5bcc5a0f6f9c98e3434196dc10257
- 将6387 “主” 节点的槽号清空,重新分配,本例将清出来的槽号都给 6381
重新分派槽号,
命令:redis-cli --cluster reshard IP地址:端口号
(任意端口集群当中的那个“主”节点的端口都行,只是以这个端口作为,入口进入而已。)
redis-cli --cluster reshard 192.168.76.149:6381
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096
How many slots do you want to move (from 1 to 16384)? 4096
What is the receiving node ID? f9d44d811ac667b4c458d36938711e698ce12f94
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: 049d3835148bb72950d87eaea955bbfe9f7132ad
Source node #2: done
删除选 done, 添加选择 all
Do you want to proceed with the proposed reshard plan (yes/no)? yes
- 进入到 Redis 容器实例当中,检查集群情况第二次
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6382
- 进入到 Redis 容器实例当中将 6387 主节点删除
命令:redis-cli --cluster del-node ip:端口 6387节点ID
root@localhost:/data# redis-cli --cluster del-node 192.168.76.149:6387 049d3835148bb72950d87eaea955bbfe9f7132ad
- 检查集群情况第三次
root@localhost:/data# redis-cli --cluster check 192.168.76.149:6382
4. 最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”