简单来讲,就是讲存放在一台数据库上的数据分布到多台数据库上,形成了一个分布式数据库,大致我们数据的拆分方式分为两种
常用于我们的微服务设计中,不同的业务领域存放不同的表,比如用户模块存放我们用户相关表,外部调用通过服务访问用户模块,用户模块再去访问对应的数据库
跨库来实现我们数据的join连接,就会导致查询性能极大的下降
垂直切分的优缺点
优点
缺点
明确的id查询是通过表的存入规则来进行匹配,如果是范围查询,将数据在两个表都进行查询然后进行业务合并
优点
缺点
垂直和水平都要面临的问题(一定是先垂直后水平)
针对多数据源的管理问题,主要有两种思路
1、客户端模式:只要需要配置好底层的数据源,然后在程序上直接访问即可
2、中间代理模式:由中间代理管理所有数据源,开发人员完全不用关心底层数据源是什么,在哪里,开发人员不用关系拆分规则
基于这两种模式对应的成熟的三方中间件
逻辑库(Schema)
将分开的物理库合并的一个逻辑数据库
逻辑表(table)
逻辑表就是物理表的总和
只要进行了水平切分就是一个分片表,没有切分的就是非分片表
通过冗余方式复制到所有分片表所在库的表就叫全局表
分片节点(dataNode)
数据表被分片到不同的分片数据库上,每个分片表所在的库就叫数据节点
分片主机(dataHost)
所有分片数据实际存放的物理数据库
分片规则(rule)
MyCat有很多分片规则,基本够用,自己本身是用Java开发的
全局序列号
http://www.mycat.org.cn/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49YRnM5c-1642657343109)(数据切分的设计方案.assets/image-20200624200530686.png)]
这是MyCat的物理结构,MyCat本身是不存储数据的
# 需要提前安装两个数据库:这两个数据库是独立,不能是主从关系
# 0.下载mycat
wget http://dl.mycat.org.cn/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz
# 1.解压
tar -xvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz
# 2.配置server.xml
cd /usr/local/mycat/mycat/conf
vi server.xml
进入server.xml我们去到底部先看用户权限
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">user_module,cart_module,product_moduleproperty>
user>
开始配置逻辑库:schema.xml
先配置两个物理主机
<dataHost name="DB213" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="M1" url="192.168.0.213:3306" user="gavin"
password="123456">
writeHost>
dataHost>
<dataHost name="DB214" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="M1" url="192.168.0.214:3306" user="gavin"
password="123456">
writeHost>
dataHost>
配置dataNode的数据节点,这里的database是我们的物理存放数据的数据库名
<dataNode name="dn213" dataHost="DB213" database="user_213" />
<dataNode name="dn214" dataHost="DB214" database="user_214" />
配置schema节点,schema节点的name是server.xml里配置逻辑库名,要对应
table节点里配置的name是物理数据库存放的实际表名
<schema name="user_module" checkSQLschema="true" sqlMaxLimit="100">
<table name="user_info" dataNode="dn213,dn214" rule="auto-sharding-long" />
schema>
现在去两个物理数据库创建两个库user_213,user_214和两个表user_info
CREATE TABLE `user_info` (
`id` int(11) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
启动MyCat
# 报错
jvm 1 | Caused by: io.mycat.config.util.ConfigException: Illegal table conf : table [ USER_INFO ] rule function [ rang-long ] partition size : 3 > table datanode size : 2, please make sure table datanode size = function partition size
# 根据分片规则去找具体的分片策略
vi rule.xml
# 找到这个规则 auto-sharding-long
<tableRule name="auto-sharding-long">
<rule>
<columns>id</columns>
<algorithm>rang-long</algorithm>
</rule>
</tableRule>
<function name="rang-long"
class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txt</property>
</function>
vi autopartition-long.txt
0-2000000=0
2000001-4000000=1
4000001-8000000=2
# K=1000,M=10000
# 需要根据dataNode设置节点数
MyCat用户名,密码,权限,Schema关系
如果一个用户下右多个Schema就以csv格式来写入
多个schema就需要在schema.xml里配置多个组schema
mysql -umycat -p -h192.168.0.211 -P8066
dataHost节点
<dataHost name="DB214" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
# name:主机名,自己命名即可
# maxCon:最大连接
# minCon:最小链接
# balance:负载均衡策略,值有4个
# 1. balance="0",不开启读写分离,所有的操作都在writeHost上操作
# 2. balance="1",全部的readHost与stand by writeHost参与select数据的负载均衡
# 3. balance="2" 所有的读操作会随机writeHost和readHost
# 4. balance="3" 所有的读操作都会随机分发到readHost,writeHost不参与读操作
双主也需要我们自己进行replica,mycat平时只写入一个主机
# writeType 写数据类型
# 1. writeType="0",所有写操作都会发送到配置的第一个writeHost,如果第一个挂了就会自动切到第二个writeHost配置上
# writeType="1",所有的写操作会随机到writeHost上,1.5版本后不推荐
# switchType 切换类型配套我我们的writeType来进行操作的
# -1 表示写操作不自动进行切换
# 1 默认值,自动切换,从第一个到第二个writeHost
# 2 基于MySQL的主从同步的状态决定是否切换
dataNode节点
<dataNode name="dn213" dataHost="DB213" database="user_213" />
# name:节点名
# dataHost:对应dataHost的名字
# database:是物理数据库的保存名称
schema节点
<schema name="user_module" checkSQLschema="true" sqlMaxLimit="100">
<table name="user_info" dataNode="dn213,dn214" rule="auto-sharding-long" />
</schema>
# name:在server配置里定义的逻辑库名
# checkSQLschema:如果是true会自动去掉数据库前缀
# sqlMaxLimit:为了减轻数据库压力,做的输出限制,默认限制100行
# sqlMaxLimit仅对分片表有效
# table name:物流表名
# rule就是分片规则
看下启动命令
Usage: ./mycat { console | start | stop | restart | status | dump }
修改完配置后不需要重启mycat就能使用?
# 更改一些基本配置可以使用脚本操作
# 比如更改了limit就可以使用
reload @@config;
# 比如更新了数据源就需要通过config_all来进行操作了,但这里是更新所有配置,比较慢
reload @@config_all;
<dataHost name="DB213" maxCon="1000" minCon="10" balance="2"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="M1" url="192.168.0.213:3306" user="gavin"
password="123456">
<readHost host="S1" url="192.168.0.211:3306" user="gavin" password="123456"/>
</writeHost>
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
<dataHost name="DB213" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="M1" url="192.168.0.213:3306" user="gavin"
password="123456">
</writeHost>
<writeHost host="M2" url="192.168.0.211:3306" user="gavin" password="123456"/>
</dataHost>
作业1:自己配置两个双主的writeHost,每个writeHost上挂载一个readHost
# columns对应我们的分片列名
<tableRule name="sharding-by-intfile">
<rule>
<columns>type</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
# 加入了默认的配置项
<function name="hash-int"
class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txt</property>
<property name="defaultNode">0</property>
</function>
# 枚举规则
vi partition-hash-int.txt
10000=0
10010=0
10020=0
10001=1
10011=1
10021=1
DEFAULT_NODE=0
# 修改schema
<schema name="user_module" checkSQLschema="true" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="user_info" dataNode="dn213,dn214" rule="sharding-by-intfile" />
# 拿id做取模分片
<tableRule name="mod-long">
<rule>
<columns>id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
# 根据节点数取模
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
</function>
# 修改schema文件
<schema name="user_module" checkSQLschema="true" sqlMaxLimit="100">
<!-- auto sharding by id (long) -->
<table name="user_info" dataNode="dn213,dn214" rule="mod-long" />
# 创建表
CREATE TABLE `login_info` (
`id` int(11) NOT NULL,
`username` varchar(255) DEFAULT NULL,
`login_date` date DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 在rule.xml中创建一个新的时间规则
<tableRule name="my-sharding-by-date">
<rule>
<columns>login_date</columns>
<algorithm>my-sharding-by-date</algorithm>
</rule>
</tableRule>
# 增加一个function
<function name="my-sharding-by-date"
class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2020-06-20</property>
<property name="sEndDate">2020-06-23</property>
<property name="sPartionDay">1</property>
</function>
# cloumns:分区字段
# algorithm:指定分片算法
# dateFormat:日期格式
# sBeginDate:开始时间
# sEndDate:结束时间
# sPartionDay:分区的大小,1代表一天分一个,如果超过结束时间则循环写入
对于数据量不大的基础配置表就没必要横向拆分
<table name="province_info" dataNode="dn213,dn214" type="global"/>
比如我们的订单信息,一般分为订单info表,订单item表,这两个表即便是按照一样的分区规则,也有可能到导致数据去到不同库,这个时候就会导致跨库
# 创建两个关联表
CREATE TABLE `order_info` (
`id` int(11) DEFAULT NULL,
`order_total` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `order_item` (
`id` int(11) DEFAULT NULL,
`order_id` int(11) DEFAULT NULL,
`product_name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 修改schema
<table name="order_info" dataNode="dn213,dn214" rule="mod-long">
<childTable name="order_item" joinKey="order_id" parentKey="id"/>
</table>
vi server.xml
<user name="customer">
<property name="password">123456</property>
<property name="schemas">user_module</property>
<property name="readOnly">true</property>
<property name="benchmark">1</property>
</user>
# readOnly只读设置
# benchmark:当链接达到这里设置的值就拒绝访问,0或不设置就是不拒绝
schema中的表的操作权限
<user name="mycat" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">user_module</property>
<!-- 表级 DML 权限设置 -->
<privileges check="true">
<schema name="user_module" dml="1111" >
<table name="province_info" dml="1110"></table>
<table name="order_info" dml="1101"></table>
</schema>
</privileges>
</user>
# insert(0/1),update(0/1),select(0/1),delete(0/1) 基于schema下的表的具体权限
# 先设置白名单
<firewall>
<whitehost>
<host host="192.168.0.213" user="mycat"/>
<host host="192.168.0.214" user="customer"/>
</whitehost>
</firewall>
# 黑名单
<firewall>
<whitehost>
<host host="192.168.0.213" user="mycat"/>
</whitehost>
<blacklist check="true">
<property name="deleteAllow">false</property>
</blacklist>
</firewall>
# 黑名单的权限配置项,是允许用户进行操作
selectAllow true/false
deleteAllow true/false
updateAllow true/false
insertAllow true/false
MyCat对于每个数据节点都可以实现HA和负载均衡,所有数据节点形成了一个分布式的数据库
如果MyCat作为一个中间层挂了怎么办?
如果要对MyCat进行集群化:MyCat是不是无状态的节点?
Nginx/HAProxy/Lvs/SLB + MyCat
如果多个MyCat,你的配置如何同时更新:zookeeper帮助我们进行配置的统一管理
# 1.修改conf下的myid.properties
loadZk=true
zkURL=192.168.0.215:2181
clusterId=mycat-cluster-1
myid=mycat_fz_01
clusterSize=2
clusterNodes=mycat_fz_01,mycat_fz_02
#server booster ; booster install on db same server,will reset all minCon to 2
type=server
boosterDataHosts=dataHost1
# 2.将之前单机的配置 server.xml,schema.xml,rule.xml,autopartition-long.txt
# 将这些修改后的配置文件传输到mycat/conf/zkconf目录下来做上传使用
# 3.执行zookeeper配置的同步脚本,在mycat/bin目录下
./init_zk_data.sh
# 报错
-bash: ./init_zk_data.sh: /bin/bash^M: bad interpreter: No such file or directory
# 修改一下命令
sed -i 's/\r$//' init_zk_data.sh