DBA MySQL MyCat中间件

MyCat简介

​ MyCat是目前最流行的基于Java语言编写的数据库中间件,是一个实现了MySQL协议的服务器,前端用户可以将它看做是一个数据库代理,用MySQL客户端工具和命令对其进行访问,其核心功能是分库分表,配合数据库的主从模式还可以实现读写分离。

​ 它的后端不仅仅可以支持MySQL,还可以支持SQL Server、Oracle、DB2、PostgreSQL等主流数据库,并且还支持MongoDB这种新型的NoSQL存储方式。

​ 在学习MyCat之前,我们先要了解一下为何要使用它,那么就得从下一小节的架构演变开始说起。

架构演变

单机时代

​ 最早的时候,单机单服务,若服务器发生故障,则业务也会受到影响:

image-20210317233701447

主从单活

​ 有没有什么好的办法能够解决这种状况呢?主从复制诞生。

​ 主从复制解决了单机提供服务的问题,从库数据和主库一模一样,主库宕机后从库顶上,但是每次主库发生故障后都需要手动进行切换,十分繁琐。

​ 于是MHA诞生了,它可以检测主库宕机后让从库自动顶上,而且可以监听多组主从,十分的方便(缺点就是一次性服务):

image-20210317234102072

读写分离

​ 看起来上面的方案已经十分完美了,但是三台服务器中只有一台提供对外服务,读写都在主库上,让硬件资源造成了极大的浪费,真正意义上的主库有难从库围观。

​ 有没有什么方案能让主库只负责写,剩下的两个从库来处理读操作呢?

​ 随之而来MySQL官方提供了一种读写分离的中间件,名为MysqlProxy,但是由于各方面原因使用的人较少,在前面的文章中我们介绍了一种更好的替代产品,第三方读写分离中间件Atlas。

​ 现在,我们已经能成功解决硬件资源利用率低的问题:

image-20210317235418887

分片集群

​ 互联网的发展总是迅猛的,数据量级的提升是飞速的,如果仅用上面的架构来存储超大量级的数据,假如有6亿条数据,即使在有索引的情况下查询也会十分缓慢,因为主库和从库的数据都一致,不管从哪个节点上进行读取,速度都会非常慢。

​ 有没有什么好的方案进行解决呢?有,我们可以将6亿数据拆成3份,再配以多个MHA架构节点进行分片处理,如下图所示,mycat中间件能够提供读写分离机制+数据分片策略,下图应该算是一个比较不错的解决方案:

image-20210317233217083

​ 如果分片1上的master挂掉之后,则分片1的slave将在MHA控制下自动顶替上,其他的分片组同理。

​ 同时,如果用户发起的是读取操作,mycat中间件会根据分片策略在不同节点上从库中进行读取,如果是写入操作,则会根据分片策略在不同节点上的主库中进行写入。

MyCat切片

垂直拆分

​ 将一个原本存储于单库上的数据存储在多库中。

​ 如图所示:

image-20210319150558063

​ 原本应用对三个表的所有请求都只在一个库中完成,该库压力比较大。

​ 通过垂直拆分,将这三个表拆成三个独立的库中存放,那么应用对不同表的操作其实就会分发到单独的库中进行处理。

​ 但是无法解决单表数据量太大的问题。

水平拆分

​ 将单表的数据拆分到多表中,如下图所示:

image-20210319150855700

​ 这样做的好处会提升单表的并发能力,且能提升磁盘I/O性能。

​ 缺点是无法提供表连接查询。

MyCat概念

逻辑库

​ MyCat中定义的库,物理中并不存在,仅针对纵向切片提供的概念:

image-20210319151242969

逻辑表

​ MyCat中定义的表,物理中并不存在,仅针对横向分片提供的概念:

image-20210319151646899

主从搭建

规划配置

​ 由于MHA服务需要多台虚拟机,所以这里演示不搭建上面的MHA架构,仅用最简单的1主1从配合mycat来进行读写分离和分库分表的应用。

​ 地址规划如下:

Master : 192.168.1.120
Slave : 192.168.1.121

mysql安装

​ 首先要在两台机器上安装mysql,先下载:

T > cd ~
T > wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.28-linux-glibc2.12-x86_64.tar.gz

​ 下载完成后,进行解压操作:

T > tar -zxvf mysql-5.7.28-linux-glibc2.12-x86_64.tar.gz -C /usr/local

​ 解压完成后重命名:

T > mv /usr/local/mysql-5.7.28-linux-glibc2.12-x86_64 /usr/local/mysql

​ 进入/usr/local文件夹,创建数据文件目录和日志文件目录:

T > cd /usr/local/
T > mkdir -p logs
T > mkdir -p data

​ 创建mysql用户和用户组:

T > groupadd mysql
T > useradd mysql -g mysql

​ 修改权限:

T > chown -R mysql:mysql ./mysql
T > chown -R mysql:mysql ./data
T > chown -R mysql:mysql ./logs

​ 设置配置文件

T > vim /etc/my.cnf

[mysqld]
user=mysql
server_id=2  # 主库改1,从库改2
port=3306
basedir=/usr/local/mysql
datadir=/usr/local/data

log_error=/usr/local/logs/mysqld.log
log_bin=/usr/local/logs/mysql_bin

binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1

slow_query_log=1
long_query_time=10
slow_query_log_file=/usr/local/logs/slow.log
log_queries_not_using_indexes

character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
default-storage-engine=INNODB

socket=/tmp/mysql.sock

​ 设置sys服务:

cat >/etc/systemd/system/mysqld.service <<EOF
[Unit]
Description=MySQL Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE = 5000
EOF

​ 设置环境变量:

T > vim /etc/profile

export PATH=/usr/local/mysql/bin:$PATH

T > source /etc/profile

​ 进行免密初始化:

T > mysqld --initialize-insecure

​ 开启mysqld.service服务:

T > systemctl start mysqld.service

​ 这一步骤仅在主库上执行,创建复制用户:

T > mysql -uroot -p -S /tmp/mysql.sock -e "GRANT REPLICATION SLAVE ON *.* TO repl@'192.168.1.%' IDENTIFIED BY '123'"

​ 接下来的步骤是从库执行的,先登录mysql:

T > mysql -uroot -p -S /tmp/mysql.sock

​ 然后是开启复制:

M > CHANGE MASTER TO MASTER_HOST='192.168.1.120',
	MASTER_USER='repl',
	MASTER_PASSWORD='123',
	MASTER_PORT=3306,
	MASTER_CONNECT_RETRY=10,
	MASTER_AUTO_POSITION=1;

​ 检查复制状态:

M > START SLAVE;

​ 至此,普通的主从搭建完成了。

安装MyCat

java环境

​ 由于我们只有一主一从,因此我打算在从库节点上安装MyCat。

​ 需要在从库节点上先安装java环境:

T > yum install java -y

MyCat

​ 趁着安装java环境的时间,我们看一看去哪里下载MyCat。

​ 刚好,我准备下载1.6.7.6版本,也是目前比较新的一版,链接地址如下,通过wget直接下载就好。

T > cd ~
T > wget http://dl.mycat.org.cn/1.6.7.6/20201104174609/Mycat-server-1.6.7.6-test-20201104174609-linux.tar.gz

​ 下载完成后,对其进行解压操作:

T > tar -zxvf Mycat-server-1.6.7.6-test-20201104174609-linux.tar.gz -C /usr/local/

​ 解压完成后,可以查看该目录下具有一个mycat文件夹了:

T > cd /usr/local
T > ll | grep mycat

drwxr-xr-x  7 root   root     85 3月  18 21:02 mycat

​ 查看目录结构:

T > ls ./mycat
bin  catlet  conf  lib  logs  version.txt

​ 目录介绍:

  • bin :启动脚本
  • conf :配置文件
  • catlet :扩展功能
  • lib :MyCat及其依赖jar
  • logs :wrapper.log保存启动日志,mycat.log保存mycat的工作日志

server.xml

打开文件

​ server.xml主要用于定义了application链接MyCat中间件的用户,以及该链接用户对逻辑库表的权限方面配置。

​ 使用vim编辑器,打开该文件,观察文件结构:

T > vim /usr/local/mycat/conf/server.xml

​ 文件节选内容如下,略有改动:

<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
        <system>
        <property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
        <property name="ignoreUnknownCommand">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。 -->
   
        <user name="root" defaultAccount="true">
                <property name="password">123456</property>
                <property name="schemas">TESTDB</property>
                <property name="defaultSchema">TESTDB</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>

        <user name="user">
                <property name="password">user</property>
                <property name="schemas">TESTDB</property>
                <property name="readOnly">true</property>
                <property name="defaultSchema">TESTDB</property>
        </user>

</mycat:server>

配置解读

​ 其实该文件的配置项非常多,但是我们仅需关注上面的即可。

​ 主要看这里:

 <user name="root" defaultAccount="true">
                <property name="password">123456</property>
                <property name="schemas">TESTDB</property>
                <property name="defaultSchema">TESTDB</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>

        <user name="user">
                <property name="password">user</property>
                <property name="schemas">TESTDB</property>
                <property name="readOnly">true</property>
                <property name="defaultSchema">TESTDB</property>
        </user>

​ 它定义了2个用户用于让application进行链接,并且密码也有所不同:

 <user name="root" defaultAccount="true">
	 <property name="password">123456</property>
 
 <user name="user">
 	<property name="password">user</property>

​ 这两个用户默认链接的虚拟库都是TESTDB:

 <property name="defaultSchema">TESTDB</property>

​ 这两个用户拥有对该虚拟库不同的权限,其中user用户是只能读,而root用户没有进行定义,则是可读可写:

 <user name="user">
	<property name="readOnly">true</property>

​ 接下来我们看一看root用户的DDL设置,注意它是注释了的:

# 权限为4位数,顺序如下:
# insert、update、select、delete

<!-- 表级 DML 权限设置 -->
<!--
    <privileges check="false">
        # TESTDB逻辑库权限:0110 则对该库不能插入,不能删除
        <schema name="TESTDB" dml="0110" >
        	# 对tb01(这是个逻辑库,但真正的默认配置文件schema.xml中没有配置)
        	# 不能增删改查
            <table name="tb01" dml="0000"></table>
            # 对tb02则可以允许全部操作
            <table name="tb02" dml="1111"></table>
        </schema>
    </privileges>
-->

schema.xml

打开文件

​ schema.xml主要定义MyCat中逻辑库与逻辑表对应的MySQL中物理库表的配置。

​ 除此之外,还有读写分离以及分片规则等配置。

​ 使用vim编辑器,打开该文件,观察文件结构:

T > vim /usr/local/mycat/conf/schema.xml

​ 文件节选内容如下,略有改动:

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

        <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
                <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
                </table>
        </schema>

        <dataNode name="dn1" dataHost="localhost1" database="db1" />
        <dataNode name="dn2" dataHost="localhost1" database="db2" />
        
        <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                
                <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"
                                   password="root">
                    <readHost host="hostS1" url="jdbc:mysql://192.168.1.121:3306" user="root"
                                   password="root">
                	</readHost>
                </writeHost>
        </dataHost>
</mycat:schema>

逻辑库表相关

​ 下面这4个标签,是比较基础的4个配置标签:

标签 描述
schema 配置逻辑库,name与server.xml中schema对应
table 配置逻辑表
dataNode 配置逻辑表中有哪些物理库
dataHost 配置物理库的读写分离策略

schema标签

​ 该标签为配置逻辑库的标签:

        <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
                ...
        </schema>

​ 主要属性如下:

  • name :逻辑库名称,与server.xml中schema对应

  • checkSQLschema :是否检测SQL语法中的schema信息,假如MyCat逻辑库名称为A,dataNode(分库的表)名称为B,如果该参数为false,则SQL查询语句为SELECT * FROM A.table;, 如果该参数为true,则查询语句为SELECT * FROM table;

  • sqlMaxLimit:如果MyCat在执行SQL查询时,没有指定limit子句,则自动添加并且值为100

table标签

​ table标签位于schema标签中,用于定义逻辑表:

        <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
                <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
                </table>
        </schema>

​ 主要属性如下:

  • name :逻辑表名称
  • dataNode :物理数据库中的数据库名称,即指定当application请求读取虚拟表时,通过哪些物理库来找这张真实表
  • rule :分片规则,具体规则请参照rule.xml文件

dataNode标签

​ dataNode标签与table标签中的dataNode属性做对应,它的作用是指定一个真实的物理库,将多个物理库加入到逻辑库中,当application请求读取虚拟表时,通过哪些物理库来找这张真实表:

        <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
                <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
                </table>
        </schema>
        
		<!-- 当application请求customer表是,可以通过db1库里找这张表,也可以通过db2库找这张表 -->
        <dataNode name="dn1" dataHost="localhost1" database="db1" />
        <dataNode name="dn2" dataHost="localhost1" database="db2" />

​ 主要属性如下:

  • name :节点名称,是一个可以随意命名的逻辑名称,主要与schema标签或者table标签dataNode属性做对接
  • dataHost :与dataHost标签的name做对接,用于引用该库在复制集群中的配置信息等,如读写分离配置
  • database :指定指定逻辑表对应的真实表在哪个物理库下进行存放

dataHost标签

​ dataHost标签是与dataNode做对接,主要负责dataNode标签中物理库读写分离的相关事宜:

   <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
                <table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
                </table>
        </schema>

        <dataNode name="dn1" dataHost="localhost1" database="db1" />
        <dataNode name="dn2" dataHost="localhost1" database="db2" />
        
        <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
          ....
        </dataHost>

​ 主要属性如下:

  • name : 主要与dataNode标签的dataHost参数做对接,用于提供复制集读写策略等配置给dataNode标签的物理数据库
  • maxCon/minCon:最大链接数与最小链接数
  • writeType :0,所有写操作都发送到可用的writeHost上。1,所有写操作都随机的发送到readHost。2,所有写操作都随机的在writeHost、readHost上发。
  • dbType :数据库类型,mysql数据库
  • dbDriver :数据库驱动类型,native是MyCat自带的一种用于驱动MySQL的驱动程序
  • banlance :0,读操作都在写库中。1,当有多主多从的时候,主之间互为主从,mater1->slave1 master2->slave2 master1和master2互为主备,select会在slave1 master2 slave2随机调用。2,所有读操作将会在所有数据库中随机执行。3, 所有读操作将会在所有读数据库中执行
  • switchType :-1,表示不自动切换。1,表示自动切换,推荐使用。2,基于mysql主从同步状态切换

heartbeat标签

​ heartbeat标签位于dataHost标签中,用于MyCat对MySQL服务的心跳检测。

 <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                <heartbeat>select user()</heartbeat>
                ...
        </dataHost>

writeHost标签

​ writeHost标签位于dataHost标签中,用于定义物理库在复制集群中写时的操作:

 <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
                          writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
                <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"
                                   password="root">
                                   ....
                </writeHost>
        </dataHost>

​ 主要属性如下:

  • host :数据库命名
  • url : 数据库访问路径
  • user : 数据库访问用户名
  • password : 访问用户的密码

readHost标签

​ readHost标签位于writeHost标签中,用于定义物理库在复制集群中读时的操作:

 <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root"
                                   password="root">
                    <readHost host="hostS1" url="jdbc:mysql://192.168.1.121:3306" user="root"
                                   password="root">
                	</readHost>
                </writeHost>

主要属性如下:

  • host :数据库命名
  • url : 数据库访问路径
  • user : 数据库访问用户名
  • password : 访问用户的密码

配置图示

​ 大量的文字描述是否感觉很心累?这里有一张图供你查看:

image-20210319162726269

rule.xml

打开文件

​ rule.xml主要定义了分片规则。

​ 使用vim编辑器,打开该文件,观察文件结构:

T > vim /usr/local/mycat/conf/rule.xml

​ 文件节选内容如下,略有改动:

<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">

        <tableRule name="rule1">
                <rule>
                        <columns>id</columns>
                        <algorithm>func1</algorithm>
                </rule>
        </tableRule>
        ...
    	<function name="func1" class="io.mycat.route.function.PartitionByLong">
                <property name="partitionCount">8</property>
                <property name="partitionLength">128</property>
        </function>
        ...
        
</mycat:rule>        

tableRule标签

​ 该标签定义分片规则以及分片列,如下所示:

        <tableRule name="rule1">
                <rule>
                        <columns>id</columns>
                        <algorithm>func1</algorithm>
                </rule>
        </tableRule>

​ name表示分片规则,内嵌rule表示详细规则

​ columns表示拆分列,algorithm表示分片函数

function标签

​ 该标签定义了分片函数,如下所示:

    	<function name="func1" class="io.mycat.route.function.PartitionByLong">
                <property name="partitionCount">8</property>
                <property name="partitionLength">128</property>
        </function>

​ name为函数本身,class为指定的分片算法具体类名

​ property表示该分片算法会用到的一些属性

MyCat前戏

用户创建

​ 我们需要在复制集上创建一个用户,让MyCat通过该用户链接MySQL。

​ 只需要在主库上做以下操作即可,用户名为root,密码也为root:

T > mysql -uroot -S /tmp/mysql.sock -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' identified by 'root';"

​ 从库由于已经搭建好了复制集,也会自动的复制该用户。

库表创建

​ 我们创建一个名为db1的数据库,并且插入3条数据在里面,接下来在主库中操作:

T > mysql -uroot
M > CREATE DATABASE db1 CHARSET utf8mb4;
M > use db1;
M > CREATE TABLE t1(id INT PRIMARY KEY AUTO_INCREMENT COMMENT "主键",name CHAR(32) NOT NULL COMMENT "姓名");
M > INSERT INTO t1(name) VALUES("Jack"),("Ken"),("Tom");

管理命令

​ 以下是MyCat的基础管理命令。

​ 启动MyCat:

/usr/local/mycat/bin/mycat start

​ 停止命令:

/usr/local/mycat/bin/mycat stop

​ 重启命令:

/usr/local/mycat/bin/mycat restart

​ 查看状态:

/usr/local/mycat/bin/mycat status

如何排错

​ MyCat的错误大多集中在启动时。

​ 如发现服务启动不成功,则可以查看wrapper.log这个启动日志。

​ 里面关键的错误信息都是使用中文编写,十分的方便。

读写分离

schema.xml配置

​ 配置文件如下:

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">  
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" dataNode="dn1">
</schema>  
        <dataNode name="dn1" dataHost="mycatLocal1" database= "db1" />         
        <dataHost name="mycatLocal1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">    
                <heartbeat>select user()</heartbeat>  
        <writeHost host="hostM1" url="192.168.1.120:3306" user="root" password="root"> 
                        <readHost host="hostS1" url="192.168.1.121:3306" user="root" password="root" /> 
        </writeHost> 
        </dataHost>  
</mycat:schema>
  • ​ balance=”0”, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上

  • ​ balance=”1”,全部的readHost与stand by writeHost参与select语句的负载均衡

  • ​ balance=”2”,所有读操作都随机的在writeHost、readhost上分发

  • ​ balance=”3”, 所有读请求随机的分发到writeHost对应的readhost执行,writerHost 不负担读压力

功能测试

​ 接下来启动MyCat:

/usr/local/mycat/bin/mycat start
<property name="serverPort">8066</property> <property name="managerPort">9066</property>

​ 登录主库中,查看MyCat是否链接主库:

M > show processlist;
+----+------+---------------------+------+------------------+-------+---------
| Id | User | Host                | db   | Command          | Time  | State                                                         | Info             |
+----+------+---------------------+------+------------------+-------+---------
|  5 | repl | 192.168.1.121:47766 | NULL | Binlog Dump GTID | 15897 | Master has sent all binlog to slave; waiting for more updates | NULL             |
| 20 | root | 192.168.1.106:53245 | db1  | Sleep            |  2347 |                                                               | NULL             |
| 21 | root | localhost           | NULL | Query            |     0 | starting                                                      | show processlist |

​ 使用Navicat链接MyCat中间件:

image-20210319005844580

image-20210319015723080

​ 现在,我们对于物理库db1(逻辑库TESTDB)做了一个读写分离。

​ 创建一个t2表,然后插入2条数据:

image-20210319021309409

​ 查看MyCat的mycat.log日志(太乱了,该兴趣的自己看吧)。

注意事项

​ 在这个schema.xml的配置文件中,并没有配置虚拟表,而是直接指定了一个虚拟库,使用dataNode参数对其做了物理库的指定。

​ 如果你用table虚拟表来指定dataNode的话,是无效的,这会导致MyCat打不开其中的任何表。

​ 如我对配置文件中进行了下面的修改:

<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
    <table name="mycatTable001" dataNode="dn1"></table>
</schema>  

​ 在我测试时,发现打不开表,所以这是一个坑点吧:

image-20210319015231820

​ 另外,使用MyCat搭建读写分离,仅能针对某个数量的库进行读写分离(取决于虚拟库的dataNode参数),而不能针对全库。

​ 相较于Atlas的全库读写分离,这一点希望能有所改进。

分库分表

垂直分库

​ 因为节点的原因,所以我们这里不再进行试验而是进行演示操作。

​ 假如我们本来有一个商城bazaar库,在里面存了orders、users、goods这3张表。

​ 此时想对bazaar库中的3张表进行拆分,要怎么做呢?

​ 如下图所示:

image-20210319155216288

​ 完美方案是MHA集群架构,总共需要9台服务器(如果不做MHA则3台足以),在各个MHA集群上的Master中添加bazaar库,并且在其中添加3张表:

server1 -> bazaar  
server2 -> bazaar  
server3 -> bazaar

server1 -> bazaar -> users
server2 -> bazaar -> orders
server3 -> bazaar -> goods

​ 然后需要到schema.xml中进行配置:

<schema xmlns:mycat="http://io.mycat/">
	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
		<!-- 如果用户读写虚拟库TESTDB中的3个表中的任意一个 -->
		<table name="users" dataNode="sh1" />
		<table name="orders" dataNode="sh2" />
		<table name="goods" dataNode="sh3" />
	</schema>
	<!-- 都会走以下的策略,如读取users表会在repSet1的bazaar库中查找 -->
	<dataNode name="sh1" dataHost="repSet1" database="bazaar" />
	<dataNode name="sh2" dataHost="repSet2" database="bazaar" />
	<dataNode name="sh3" dataHost="repSet3" database="bazaar" />
	<!-- users的读写配置 -->
	<dataHost name="repSet1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.110:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.111:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- orders的读写配置 -->
	<dataHost name="repSet2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.120:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.121:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- goods的读写配置 -->
	<dataHost name="repSet3" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.130:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.131:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
</schema>

水平分表

​ 我们继续对orders做一个水平分表。

​ 同样的,如果该表中有6000万数据,我们需要将表分成3分,又要用到3个MHA集群。

​ 在所有MHA集群主库上同样创建3个bazaar库:

server2 -> bazaar   # 如果按照上面的策略继续往下做,就不用创建这个库了
server2_1 -> bazaar  
server2_2 -> bazaar

​ 并且在3个节点的bazaar库中,创建3个orders表:

server2 -> bazaar -> users   # 如果按照上面的策略继续往下做,就不用创建这个库了
server2_1 -> bazaar -> orders
server2_2 -> bazaar -> goods

​ 如下图所示:

image-20210319155720498

​ 然后需要到schema.xml中进行配置:

<schema xmlns:mycat="http://io.mycat/">
	<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
		<!-- 如果用户读写虚拟库TESTDB中的3个表中的任意一个 -->
		<table name="users" dataNode="sh1" />
		<!-- orders指定分片策略,按照id进行分片 -->
		<table name="orders" dataNode="sh2,sh4,sh5" rule="auto-sharding-long" />
		<table name="goods" dataNode="sh3" />
	</schema>
	<!-- 都会走以下的策略,如要访问orders表,则由repSet2,4,5的bazaar中的3个orders表组成逻辑表 -->
	<dataNode name="sh1" dataHost="repSet1" database="bazaar" />
	<dataNode name="sh3" dataHost="repSet3" database="bazaar" />
	<!-- orders水平拆分的集群与物理库 -->
	<dataNode name="sh2" dataHost="repSet2" database="bazaar" />
	<dataNode name="sh4" dataHost="repSet4" database="bazaar" />
	<dataNode name="sh5" dataHost="repSet5" database="bazaar" />
	<!-- 访问users,读写配置 -->
	<dataHost name="repSet1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.110:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.111:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- 访问goods,读写配置 -->
	<dataHost name="repSet3" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.130:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.131:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- 访问orders,读写配置 -->
	<!-- 水平分片1 -->
	<dataHost name="repSet2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.120:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.121:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- 水平分片2 -->
	<dataHost name="repSet2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.140:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.141:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
	<!-- 水平分片3 -->
	<dataHost name="repSet2" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">
		<heartbeat>
			select user()
		</heartbeat>
		<writeHost host="repMaster" url="192.168.1.150:3306" user="root" password="root">
			<readHost host="repSlave" url="192.168.1.151:3306" user="root" password="root" />
		</writeHost>
	</dataHost>
</schema>

至此,水平分表构建完成。

拆分策略

指定片键

​ 我们以范围拆分为例,如果想指定拆分的键(上面是id),可以打开配置文件rule.xml中修改即可:

<tableRule name="auto-sharding-long">
<rule>
    <columns>id</columns>
    <algorithm>rang-long</algorithm>
</rule>
</tableRule>

常见拆分

​ 针对拆分策略,有以下的几种常见的:

策略 描述
auto-sharding-long range,范围拆分
mod-long 取余
sharding-by-intfile 枚举
hash 哈希
time 时间

​ 若想了解更多,可参见如下文章:

点我跳转

工作管理

基本概述

​ MyCat具有工作端口和管理端口之分。

​ 工作端口号为8066,也是application链接的端口号。

​ 管理端口号为9066,可对MyCat配置与节点等做出管控。

相关文章

​ 由于篇幅原因,这里不再介绍,如果你感兴趣,可参见如下文章:

点我跳转

posted @ 2021-03-19 16:13  云崖君  阅读(176)  评论(0编辑  收藏  举报