MySQL 学习笔记(运维篇2)

一、分库分表(延续上节博客的内容)

1. 垂直拆分

(1) 场景

(2) 配置

XML 复制代码
<schema name="SHOPPING" checkSQLschema="true" sqlMaxLimit="100" >
    <table name="tb_goods_base" dataNode="dn1" primaryKey="id"/>
    <table name="tb_goods_cat" dataNode="dn1" primaryKey="id"/>
    <table name="tb_goods_desc" dataNode="dn1" primaryKey="goods_id"/>
    <table name="tb_goods_item" dataNode="dn1" primaryKey="id"/>
    <table name="tb_order_item" dataNode="dn2" primaryKey="id"/>
    <table name="tb_order_master" dataNode="dn2" primaryKey="order_id" rule="order_rule" />
    <table name="tb_order_pay_log" dataNode="dn2" primaryKey="out_trade_no"/>
    <table name="tb_user" dataNode="dn3" primaryKey="id"/>
    <table name="tb_areas_provinces" dataNode="dn3" primaryKey="id"/>
    <table name="tb_areas_city" dataNode="dn3" primaryKey="id"/>
    <table name="tb_areas_region" dataNode="dn3" primaryKey="id"/>
    <table name="tb_user_address" dataNode="dn3" primaryKey="id"/>
</schema>

<dataNode name="dn1" dataHost="dhost1" database="shopping" />
<dataNode name="dn2" dataHost="dhost2" database="shopping" />
<dataNode name="dn3" dataHost="dhost3" database="shopping" />

说明:

schema 节点下关联多个 table 标签,不同 table 对应不同 dataNode:

① dn1 对应商品类表(如 tb_goods_base、tb_goods_cat 等)

② dn2 对应订单类表(如 tb_order_item、tb_order_master 等)

③ dn3 对应用户及地址类表(如 tb_user、tb_areas_provinces 等)

(3) 测试

① 在 mycat 的命令行中,通过 source 指令导入表结构及对应数据,查看数据分布情况:

bash 复制代码
source /root/shopping_table.sql
source /root/shopping_insert.sql

② 查询用户的收件人及收件人地址信息 (包含省、市、区):

sql 复制代码
select ua.user_id,ua.contact,p.province,c.city,r.area,ua.address from tb_user_address ua,tb_areas_city c,tb_areas_provinces p,tb_areas_region r where ua.province_id = p.provinceid and ua.city_id = c.cityid and ua.town_id = r.areaid ;

③ 查询每一笔订单及订单的收件地址信息 (包含省、市、区):

sql 复制代码
SELECT o.order_id, p.payment,o.receiver,p.province,c.city,r.area FROM tb_order_master o,tb_areas_provinces p,tb_areas_city c,tb_areas_region r WHERE o.receiver_province = p.provinceid AND o.receiver_city = c.cityid AND o.receiver_region = r.areaid ;

(4) 全局表配置

对于省、市、区 / 县表tb_areas_provinces, tb_areas_city, tb_areas_region,是属于数据字典表,在多个业务模块中都可能会遇到,可以将其设置为全局表,利于业务操作。

XML 复制代码
<table name="tb_areas_provinces" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_city" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
<table name="tb_areas_region" dataNode="dn1,dn2,dn3" primaryKey="id" type="global"/>
2. 水平拆分

(1) 场景

在业务系统中,有一张表 (日志表),业务系统每天都会产生大量的日志数据,单台服务器的数据存储及处理能力是有限的,可以对数据库表进行拆分。

(2) 配置

① shema.xml

XML 复制代码
<schema name="ITCAST" checkSQLschema="true" sqlMaxLimit="100" >
    <table name="tb_log" dataNode="dn4,dn5,dn6" rule="mod-long" />
</schema>
<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />

② server.xml

XML 复制代码
<user name="root" defaultAccount="true">
    <property name="password">123456</property>
    <property name="schemas">SHOPPING,ITCAST</property>
    <!-- 表级 DML 权限设置 -->
    <!--
    <privileges check="false">
        <schema name="TESTDB" dml="0110">
            <table name="tb01" dml="0000"></table>
            <table name="tb02" dml="1111"></table>
        </schema>
    </privileges>
    -->
</user>

(3) 测试

在 mycat 的命令行中,执行如下 SQL 建表、并插入数据,查看数据分布情况。

sql 复制代码
CREATE TABLE tb_log (
id bigint(20) NOT NULL COMMENT 'ID',
model_name varchar(200) DEFAULT NULL COMMENT '模块名',
model_value varchar(200) DEFAULT NULL COMMENT '模块值',
type varchar(200) DEFAULT NULL COMMENT '类型',
return_value varchar(200) DEFAULT NULL COMMENT '返回值',
return_class varchar(200) DEFAULT NULL COMMENT '返回类型',
operate_user varchar(200) DEFAULT NULL COMMENT '操作用户',
operate_time timestamp NULL DEFAULT NULL COMMENT '操作时间',
param_and_value varchar(500) DEFAULT NULL COMMENT '请求参数名及参数值',
operate_class varchar(200) DEFAULT NULL COMMENT '操作类',
operate_method varchar(200) DEFAULT NULL COMMENT '操作方法',
cost_time bigint(20) DEFAULT NULL COMMENT '执行方法耗时,单位ms',
summary text DEFAULT NULL COMMENT '摘要 1.2,Arond.102',
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 分片规则

(1) 范围分片

根据指定的字段及其配置的范围与数据节点的对应情况,来决定该数据属于哪一个分片。

说明:

  • 0~5000000 → dataNode1
  • 5000001~10000000 → dataNode2
  • 10000001 及以上 → dataNode3

(2) 取模分片

根据指定的字段值与节点数量进行求模运算,根据运算结果,来决定该数据属于哪一个分片。

说明: 通过id % 3的结果分配数据:

  • id%3=0 → dataNode1
  • id%3=1 → dataNode2
  • id%3=2 → dataNode3

(3) 一致性hash 分片

所谓一致性哈希,相同的哈希因子计算总是被划分到相同的分区表中,不会因为分区节点的增加而改变原来数据的分区位置。

① schema.xml

XML 复制代码
<table name="tb_order" dataNode="dn4,dn5,dn6" rule="sharding-by-murmur" />

<dataNode name="dn4" dataHost="dhost1" database="itcast" />
<dataNode name="dn5" dataHost="dhost2" database="itcast" />
<dataNode name="dn6" dataHost="dhost3" database="itcast" />

② rule.xml

XML 复制代码
<tableRule name="sharding-by-murmur">
    <rule>
        <columns>id</columns>
        <algorithm>murmur</algorithm>
    </rule>
</tableRule>

<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
    <property name="seed">0</property> <!-- 默认是0 -->
    <property name="count">3</property> <!-- 要分片的数据库节点数,必须指定,否则没法分片 -->
    <property name="virtualBucketTimes">160</property> <!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
    <!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。 -->
</function>

(4) 枚举分片

通过在配置文件中配置可能的枚举值,指定数据分布到不同数据库节点上。本规则适用于按照省份、性别、状态拆分数据等业务。

(5) 应用指定算法

运行阶段由应用自主决定路由到哪个分片,直接根据字符串(必须是数字)计算分片号。

说明:

  • 00xxxx → 对应 dataNode1 的数据库
  • 01xxxx → 对应 dataNode2 的数据库
  • 02xxxx → 对应 dataNode3 的数据库

(6) 固定hash算法

该算法类似于十进制的求模运算,但是为二进制的操作,例如,取 id 的二进制低 10 位与1111111111进行位 & 运算。

说明:

① 任何一个十位的二进制与 1111111111(十位全1)进行与运算后的结果,结果就是这个十位二进制数本身。

② 原因是这个二进制数的每一位都是 1,因此在与运算中:

  • 如果原数某一位是 1 → 1 & 1 = 1
  • 如果原数某一位是 0 → 0 & 1 = 0

也就是说,它不会改变原数的任何一位,只是保留原数的所有位。

③ 举例验证:假设原数是 1010101010(十位):

bash 复制代码
    1010101010
  & 1111111111
  -----------
    1010101010  ← 结果和原数完全相同

(7) 字符串hash解析

截取字符串中的指定位置的子字符串,进行hash算法,算出分片

运算示例:

以 "world" 为例:

  • 截取子串:world → 取0:2对应 "wor"
  • 执行 hash 运算:得到结果 26629
  • 计算分片:26629 & (1024-1) = 5

(8) 按天分片

说明:

配置参数: begin:2022-01-01end:2022-01-30partitionDay:10

分片时间区间与 dataNode 对应:

  • 2022-01-01 ~ 2022-01-10 → dataNode1
  • 2022-01-11 ~ 2022-01-20 → dataNode2
  • 2022-01-21 ~ 2022-01-30 → dataNode3

(9) 按月分片

使用场景为按月份来分片,每个自然月为一个分片

说明:

分片时间范围为 begin:2022-01-01end:2022-03-31,数据按月份对应到不同 dataNode:

  • dataNode1 对应数据:2022-01-10、2022-04-12(注:结束时间后会循环分片)
  • dataNode2 对应数据:2022-02-20
  • dataNode3 对应数据:2022-03-31

​​​​​​​

4. Mycat 管理及监控

(1) Mycat 原理

select * from tb_user;

  • 客户端把 SQL 发给 Mycat Server;
  • Mycat 先解析 SQL ,再做分片分析(按规则,这个 SQL 要查所有分片);
  • 路由分析,确定要访问右边 3 个数据库节点;
  • 从 3 个节点取数据后,做分页、排序、聚合、结果合并
  • 把合并后的结果返回给客户端。

select * from tb_user where status in(1,3) order by id;

  • 客户端发 SQL 后,Mycat 解析出status条件;
  • 分片分析 + 路由分析:只需要访问status=1(dn1)和status=3(dn3)的数据库;
  • 从这 2 个节点取数据后,做排序处理 + 结果合并
  • 返回最终结果给客户端。

(2) Mycat 管理

① Mycat 默认开通 2 个端口,可在server.xml中修改:

  • 8066 数据访问端口,用于执行 DML 和 DDL 操作。
  • 9066 数据库管理端口,用于管理 Mycat 集群状态。

② 连接管理端口的命令:

bash 复制代码
mysql -h 192.168.200.210 -P 9066 -uroot -p123456

③ Mycat 管理命令及含义:

命令 含义
show @@help 查看 Mycat 管理工具文档
show @@version 查看 Mycat 的版本
reload @@config 重新加载 Mycat 的配置文件
show @@datasource 查看 Mycat 的数据源信息
show @@datanode 查看 Mycat 现有的分片节点信息
show @@threadpool 查看 Mycat 的线程池信息
show @@sql 查看执行的 SQL
show @@sql.sum 查看执行的 SQL 统计

二、读写分离

1. 介绍

(1) 读写分离,简单地说是把对数据库的读和写操作分开,对应不同的数据库服务器。主数据库提供写操作,从数据库提供读操作,这样能有效地减轻单台数据库的压力。

(2) 通过 MyCat 即可轻易实现上述功能,不仅可以支持 MySQL,也可以支持 Oracle 和 SQL Server。

说明:

① 应用程序连接 MyCat,MyCat 将insert/update/delete操作路由到writeHost对应的主库(mysql (m))

② 将select操作路由到readHost对应的从库(mysql (s))

③ 主库与从库之间通过主从复制(blog)同步数据

2. 一主一从读写分离

(1) 配置

Mycat 中balance参数的取值及含义说明表:

参数值 含义
0 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上
1 全部的 readHost 与备用的 writeHost 都参与 select 语句的负载均衡(主要针对于双主双从模式)
2 所有的读写操作都随机在 writeHost、readHost 上分发
3 所有的读请求随机分发到 writeHost 对应的 readHost 上执行,writeHost 不负担读压力
3. 双主双从

(1) 介绍

一个主机 Master1 用于处理所有写请求,它的从机 Slave1 和另一台主机 Master2 还有它的从机 Slave2 负责所有读请求。当 Master1 主机宕机后,Master2 主机负责写请求,Master1、Master2 互为备机。架构图如下:

说明:

① 应用程序连接 MyCat,MyCat 分发请求;

② mysql (m1)(Master1)复制数据到 mysql (s1)(Slave1),mysql (m2)(Master2)复制数据到 mysql (s2)(Slave2),同时 mysql (m1) 与 mysql (m2) 之间也存在复制关系,mysql (m1) 为主、mysql (m2) 为备

**Q:**主库不是负责写的吗?为什么 Master2 负责读了?

A: 双主双从中,Master1 是 "主用主库":默认承担所有写请求; 而 Master2 是"备用主库"

  • 平时(Master1 正常运行时),它不处理写请求,而是作为读节点来分担读压力(和 Slave1、Slave2 一起负责读请求);
  • 只有当 Master1 宕机后,它才会切换为 "主库",接手写请求。

(2) 准备工作

我们需要准备 5 台服务器,具体的服务器及软件安装情况如下:

编号 IP 预装软件 角色
1 192.168.200.210 MyCat、MySQL MyCat 中间件服务器
2 192.168.200.211 MySQL M1(Master1)
3 192.168.200.212 MySQL S1(Slave1)
4 192.168.200.213 MySQL M2(Master2)
5 192.168.200.214 MySQL S2(Slave2)

关闭以上所有服务器的防火墙:

  • systemctl stop firewalld
  • systemctl disable firewalld

(3) 搭建

① 主库配置(M1)

修改配置文件 /etc/my.cnf

bash 复制代码
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 - 2^32-1,默认为1
server-id=1
#指定同步的数据库
binlog-do-db=db01
binlog-do-db=db02
binlog-do-db=db03
#在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates

重启 MySQL 服务器

bash 复制代码
systemctl restart mysqld

② 主库配置(M2)

修改配置文件 /etc/my.cnf

bash 复制代码
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 - 2^32-1,默认为1
server-id=3
#指定同步的数据库
binlog-do-db=db01
binlog-do-db=db02
binlog-do-db=db03
#在作为从数据库的时候,有写入操作也要更新二进制日志文件
log-slave-updates

重启 MySQL 服务器

bash 复制代码
systemctl restart mysqld

③ 两台主库创建账户并授权(注:该操作需在 Master1、Master2 两台主库分别执行)

bash 复制代码
# 创建itcast用户,并设置密码,该用户可在任意主机连接该MySQL服务
CREATE USER 'itcast'@'%' IDENTIFIED WITH mysql_native_password BY 'Root@123456';

# 为'itcast'@'%'用户分配主从复制权限
GRANT REPLICATION SLAVE ON *.* TO 'itcast'@'%';

查看两台主库的二进制日志坐标

bash 复制代码
show master status;

④ 从库配置(S1)

修改配置文件 /etc/my.cnf

bash 复制代码
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 - 2^32-1,默认为1
server-id=2

重启 MySQL 服务器

bash 复制代码
systemctl restart mysqld

⑤ 从库配置(S2)

修改配置文件 /etc/my.cnf

bash 复制代码
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 - 2^32-1,默认为1
server-id=4

重启 MySQL 服务器

bash 复制代码
systemctl restart mysqld

⑥ 两台从库关联的从库

执行CHANGE MASTER TO命令,关联对应的主库(注意:slave1 对应 master1,slave2 对应 master2):

sql 复制代码
CHANGE MASTER TO 
MASTER_HOST='xxx.xxx.xxx.xxx',  # 对应主库的IP
MASTER_USER='xxx',             # 主库授权的复制用户(如itcast)
MASTER_PASSWORD='xxx',         # 复制用户的密码(如Root@123456)
MASTER_LOG_FILE='xxx',         # 主库`show master status`查到的日志文件名
MASTER_LOG_POS=xxx;            # 主库`show master status`查到的日志位置

启动两台从库主从复制,查看从库状态

sql 复制代码
# 启动主从复制
start slave;

# 查看从库状态(需确保Slave_IO_Running、Slave_SQL_Running均为Yes)
show slave status\G;

(4) 测试

分别在两台主库Master1、Master2上执行DDL、DML语句,查看涉及到的数据库服务器的数据同步情况。

sql 复制代码
create database db01;
use db01;
create table tb_user(
	id in(11)not null primary key,
	name varchar(50) not null,
	sex varcahr(1)
)engine=innodb default charset=utf8mb4

insert into tb user(id,name,sex) values(l,'Tom','1');
insert into tb user(id,name,sex) values(2,'Trigger','0');
insert into tb user(id,name,sex) values(3,'Dawn','1');
insert into tb user(id,name,sex) values(4,"ack Ma','1');
insertinto tb user(id,name,sex) values(5,'Coco','0');
insert into tb user(id,name,sex) values(6,'erry','1');
4. 双主双从读写分离

(1) 配置

(2) 测试

① 登录MyCat,测试查询及更新操作,判定是否能够进行读写分离,以及读写分离的策略是否正确。

② 当主库挂掉一个之后,是否能够自动切换。

相关推荐
luoluoal11 小时前
基于python的语音识别与蓝牙通信的温控系统(源码+文档)
python·mysql·django·毕业设计·源码
好奇龙猫11 小时前
【大学院-筆記試験練習:数据库(データベース問題訓練) と 软件工程(ソフトウェア)(6)】
学习
l1t11 小时前
将利用30行X算法求解数独的python程序转成DuckDB自定义函数并比较性能
数据库·python·算法·duckdb
千寻技术帮11 小时前
10355_基于Springboot的驾校管理系统
spring boot·mysql·vue·文档·驾校管理
d111111111d11 小时前
STM32如何通过寄存器直接禁止EXTI0中断
笔记·stm32·单片机·嵌入式硬件·学习
墨白曦煜12 小时前
硬核图解:MySQL 是如何利用 MVCC + 锁实现“可重复读”的?
数据库·mysql
YJlio12 小时前
PsPing 学习笔记(14.4):TCP/UDP 延迟测试——从单包 RTT 到抖动分析
笔记·学习·tcp/ip
DarkAthena12 小时前
【DuckDB】探索函数调用新范式:点操作符链式调用
数据库·sql·duckdb
自己的九又四分之三站台12 小时前
PG GraphQL详细介绍与基本使用
数据库·sql·graphql