二十三、Mysql读写分离之Mycat

一、mycat介绍

MyCat是一个开源的分布式数据库系统,是一个实现了MySQL协议的服务器,前端用户可以把它看作是一个数据库代理,用MySQL客户端工具和命令行访问,而其后端可以用MySQL原生协议与多个MySQL服务器通信,也可以用JDBC协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为N个小表,存储在后端MySQL服务器里或者其他数据库里。

MyCat发展到目前的版本,已经不是一个单纯的MySQL代理了,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库,也支持MongoDB这种新型NoSQL方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在MyCat里,都是一个传统的数据库表,支持标准的SQL语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度。

我们的应用只需要一台数据库服务器的时候我们并不需要Mycat,而如果你需要分库甚至分表,这时候应用要面对很多个数据库的时候,这个时候就需要对数据库层做一个抽象,来管理这些数据库,而最上面的应用只需要面对一个数据库层的抽象或者说数据库中间件就好了,这就是Mycat的核心作用。

总结:Mycat是一个广受好评的数据库中间件,为了减轻单数据库的压力,可以实现主从、热备、分表分库,从而实现数据库的分布式架构。

参考博客:https://blog.csdn.net/nxw_tsp/article/details/56277430

二、Mycat原理

Mycat的原理是用户不在直接访问后端数据库,mycat接受了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。

应用程序不再直接访问数据库,而是访问Mycat,由Mycat与数据库交互,数据库数据返回给Mycat,Mycat再返回给应用程序。三个Database才是真正的数据库,又称为三个节点,也称为三个分片。

总结:Mycat作为一个中间件,应用程序直接访问它,不用再去管真实的数据库,而由Mycat来与真实的数据库进行交互,真实的数据库可能有多个,这就是分布式架构,即多节点(多分片)

 

三、Mycat应用场景

Mycat发展到现在,适用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是几个典型的应用场景:

单纯的读写分离,此时配置最为简单,支持读写分离,主从切换分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片

多租户应用,每个应用一个库,但应用程序只连接Mycat,从而不改造程序本身,实现多租户化

报表系统,借助于Mycat的分表能力,处理大规模报表的统计

替代Hbase,分析大数据

作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要在3秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时Mycat可能是最简单有效的选择

Mycat长期路线图

强化分布式数据库中间件的方面的功能,使之具备丰富的插件、强大的数据库智能优化功能、全面的系统监控能力、以及方便的数据运维工具,实现在线数据扩容、迁移等高级功能

进一步挺进大数据计算领域,深度结合Spark Stream和Storm等分布式实时流引擎,能够完成快速的巨表关联、排序、分组聚合等 OLAP方向的能力,并集成一些热门常用的实时分析算法,让工程师以及DBA们更容易用Mycat实现一些高级数据分析处理功能。

不断强化Mycat开源社区的技术水平,吸引更多的IT技术专家,使得Mycat社区成为中国的Apache,并将Mycat推到Apache基金会,成为国内顶尖开源项目,最终能够让一部分志愿者成为专职的Mycat开发者,荣耀跟实力一起提升。

依托Mycat社区,聚集100个CXO级别的精英,众筹建设亲亲山庄,Mycat社区+亲亲山庄=中国最大IT O2O社区

参考:https://www.cnblogs.com/andy6/p/6622324.html 收录于:Mycat权威指南,感谢作者

四、mycat的前期准备

4.1mycat架构图

 

4.2mycat实验环境

192.168.32.201  db01+mysql5.7 多实例
192.168.32.202  db01+mysql5.7 多实例
192.168.32.203  node3+mycat
System OS: CentOS Linux release 7.6.1810 (Core)
Mysql version: mysql-5.7.20-linux-glibc2.12-x86_64.tar.gz 二进制部署
mycat version: Mycat-server-1.6.7.1-release-20190627191042-linux.tar

mysql相关数据目录
db01 db02
mysql软件目录  /app/mysql
mysql数据目录 /data
多实例数据目录 /data/33{07..10}

db01
3307: server_id=7
3308: server_id=8
3309: server_id=9
3310: server_id=10

db02
3307: server_id=17
3308: server_id=18
3309: server_id=19
3310: server_id=20

4.3二进制部署

参考https://www.cnblogs.com/yaokaka/p/13914362.html

4.4mysql实验环境准备

两台虚拟机 db01 db02
每台创建四个mysql实例:3307 3308 3309 3310

db01和db02操作配置一样

1、创建相关目录初始化数据

 
mkdir /data/33{07..10}/data -p
mysqld --initialize-insecure  --user=mysql --datadir=/data/3307/data --basedir=/app/mysql
mysqld --initialize-insecure  --user=mysql --datadir=/data/3308/data --basedir=/app/mysql
mysqld --initialize-insecure  --user=mysql --datadir=/data/3309/data --basedir=/app/mysql
mysqld --initialize-insecure  --user=mysql --datadir=/data/3310/data --basedir=/app/mysql

2、准备配置文件和启动脚本

db01

 
cat >/data/3307/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3307/data
socket=/data/3307/mysql.sock
port=3307
log-error=/data/3307/mysql.log
log_bin=/data/3307/mysql-bin
binlog_format=row
skip-name-resolve
server-id=7
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF

cat >/data/3308/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3308/data
port=3308
socket=/data/3308/mysql.sock
log-error=/data/3308/mysql.log
log_bin=/data/3308/mysql-bin
binlog_format=row
skip-name-resolve
server-id=8
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF

cat >/data/3309/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3309/data
socket=/data/3309/mysql.sock
port=3309
log-error=/data/3309/mysql.log
log_bin=/data/3309/mysql-bin
binlog_format=row
skip-name-resolve
server-id=9
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF
cat >/data/3310/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3310/data
socket=/data/3310/mysql.sock
port=3310
log-error=/data/3310/mysql.log
log_bin=/data/3310/mysql-bin
binlog_format=row
skip-name-resolve
server-id=10
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF

cat >/etc/systemd/system/mysqld3307.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=/app/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3308.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=/app/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3309.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=/app/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf
LimitNOFILE = 5000
EOF
cat >/etc/systemd/system/mysqld3310.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=/app/mysql/bin/mysqld --defaults-file=/data/3310/my.cnf
LimitNOFILE = 5000
EOF

db02

cat >/data/3307/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3307/data
socket=/data/3307/mysql.sock
port=3307
log-error=/data/3307/mysql.log
log_bin=/data/3307/mysql-bin
binlog_format=row
skip-name-resolve
server-id=17
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF
cat >/data/3308/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3308/data
port=3308
socket=/data/3308/mysql.sock
log-error=/data/3308/mysql.log
log_bin=/data/3308/mysql-bin
binlog_format=row
skip-name-resolve
server-id=18
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF
cat >/data/3309/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3309/data
socket=/data/3309/mysql.sock
port=3309
log-error=/data/3309/mysql.log
log_bin=/data/3309/mysql-bin
binlog_format=row
skip-name-resolve
server-id=19
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF


cat >/data/3310/my.cnf<<EOF
[mysqld]
basedir=/app/mysql
datadir=/data/3310/data
socket=/data/3310/mysql.sock
port=3310
log-error=/data/3310/mysql.log
log_bin=/data/3310/mysql-bin
binlog_format=row
skip-name-resolve
server-id=20
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
EOF

cat >/etc/systemd/system/mysqld3307.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=/app/mysql/bin/mysqld --defaults-file=/data/3307/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3308.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=/app/mysql/bin/mysqld --defaults-file=/data/3308/my.cnf
LimitNOFILE = 5000
EOF

cat >/etc/systemd/system/mysqld3309.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=/app/mysql/bin/mysqld --defaults-file=/data/3309/my.cnf
LimitNOFILE = 5000
EOF
cat >/etc/systemd/system/mysqld3310.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=/app/mysql/bin/mysqld --defaults-file=/data/3310/my.cnf
LimitNOFILE = 5000
EOF

3、修改目录权限,启动多实例

chown -R mysql.mysql /data/*
systemctl start mysqld3307
systemctl start mysqld3308
systemctl start mysqld3309
systemctl start mysqld3310

4、多实例测试

db01

 
[root@db01 ~]# mysql -S /data/3307/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 7     |
+---------------+-------+
[root@db01 ~]# mysql -S /data/3308/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 8     |
+---------------+-------+
[root@db01 ~]# mysql -S /data/3309/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 9     |
+---------------+-------+
[root@db01 ~]# mysql -S /data/3310/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 10    |
+---------------+-------+

db02

[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 17    |
+---------------+-------+
[root@db02 ~]# mysql -S /data/3308/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 18    |
+---------------+-------+
[root@db02 ~]# mysql -S /data/3309/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 19    |
+---------------+-------+
[root@db02 ~]# mysql -S /data/3310/mysql.sock -e "show variables like 'server_id'"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 20    |
+---------------+-------+

4.5、节点主从规划

 
箭头指向谁是主库
   192.168.32.201:3307    <----->   192.168.32.202:3307
   192.168.32.201:3309    ------>   192.168.32.201:3307
   192.168.32.202:3309    ------>   192.168.32.202:3307

   192.168.32.202:3308   <----->    192.168.32.201:3308
   192.168.32.202:3310   ------>    192.168.32.202:3308
   192.168.32.201:3310   ------>     92.168.32.201:3308

4.6、 分片规划

 
shard1:
    Master:192.168.32.201:3307
    slave1:192.168.32.201:3309
    Standby Master:192.168.32.202:3307
    slave2:192.168.32.202:3309
shard2:
    Master:192.168.32.202:3308
    slave1:192.168.32.202:3310
    Standby Master:192.168.32.201:3308
    slave2:192.168.32.201:3310

4.7主从关系配置

1、shard1

192.168.32.201:3307 <-----> 192.168.32.202:3307

db02

mysql  -S /data/3307/mysql.sock -e "grant replication slave on . to repl@'192.168.32.%' identified by '123';"
mysql  -S /data/3307/mysql.sock -e "grant all  on . to root@'192.168.32.%' identified by '123'  with grant option;"

db01

mysql  -S /data/3307/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.202', MASTER_PORT=3307, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3307/mysql.sock -e "start slave;"
mysql  -S /data/3307/mysql.sock -e "show slave status\G"

db02

mysql  -S /data/3307/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.201', MASTER_PORT=3307, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3307/mysql.sock -e "start slave;"
mysql  -S /data/3307/mysql.sock -e "show slave status\G"

192.168.32.201:3309 ----> 192.168.32.201:3307

db01

mysql  -S /data/3309/mysql.sock  -e "CHANGE MASTER TO MASTER_HOST='192.168.32.201', MASTER_PORT=3307, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3309/mysql.sock  -e "start slave;"
mysql  -S /data/3309/mysql.sock  -e "show slave status\G"

192.168.32.202:3309 ----> 192.168.32.202:3307

db02

 
mysql  -S /data/3309/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.202', MASTER_PORT=3307, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3309/mysql.sock -e "start slave;"
mysql  -S /data/3309/mysql.sock -e "show slave status\G"

2、shard2

192.168.32.202:3308 <-----> 192.168.32.201:3308

db01

mysql  -S /data/3308/mysql.sock -e "grant replication slave on . to repl@'192.168.32.%' identified by '123';"
mysql  -S /data/3308/mysql.sock -e "grant all  on . to root@'192.168.32.%' identified by '123'  with grant option;"

db02

mysql  -S /data/3308/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.201', MASTER_PORT=3308, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3308/mysql.sock -e "start slave;"
mysql  -S /data/3308/mysql.sock -e "show slave status\G"

db01

mysql  -S /data/3308/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.202', MASTER_PORT=3308, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3308/mysql.sock -e "start slave;"
mysql  -S /data/3308/mysql.sock -e "show slave status\G"

192.168.32.202:3310 ----> 192.168.32.202:3308

db02

mysql  -S /data/3310/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.202', MASTER_PORT=3308, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3310/mysql.sock -e "start slave;"
mysql  -S /data/3310/mysql.sock -e "show slave status\G"

192.168.32.201:3310 ----> 192.168.32.201:3308

db01

mysql  -S /data/3310/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='192.168.32.201', MASTER_PORT=3308, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='123';"
mysql  -S /data/3310/mysql.sock -e "start slave;"
mysql  -S /data/3310/mysql.sock -e "show slave status\G"

3、测试

mysql -S /data/3307/mysql.sock -e "show slave status\G"|grep Yes
mysql -S /data/3308/mysql.sock -e "show slave status\G"|grep Yes
mysql -S /data/3309/mysql.sock -e "show slave status\G"|grep Yes
mysql -S /data/3310/mysql.sock -e "show slave status\G"|grep Yes

db01

[root@db01 ~]# mysql -S /data/3307/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db01 ~]# mysql -S /data/3308/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db01 ~]# mysql -S /data/3309/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db01 ~]# mysql -S /data/3310/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

db02

[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db02 ~]# mysql -S /data/3308/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db02 ~]# mysql -S /data/3309/mysql.sock -e "show slave status\G"|grep Yes
            Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
[root@db02 ~]# mysql -S /data/3310/mysql.sock -e "show slave status\G"|grep Yes
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes

五、mycat的部署

5.1mycat下载

Mycat的官方网站 http://www.mycat.org.cn/  
https://github.com/MyCATApache/Mycat-download 
实验版本:Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz

5.2预先安装Java运行环境

[root@node3 app]# yum install -y java

5.3解压软件并配置环境变量

 
[root@node3 app]# cd /app
#上传mycat软件
[root@node3 app]# tar -xf Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz 
[root@node3 app]# cd mycat/
[root@node3 mycat]# ll
total 12
drwxr-xr-x 2 root root  190 Dec  9 05:26 bin
drwxrwxrwx 2 root root    6 Jun 24  2019 catlet
drwxrwxrwx 4 root root 4096 Dec  9 05:26 conf
drwxr-xr-x 2 root root 4096 Dec  9 05:26 lib
drwxrwxrwx 2 root root    6 Jun 26  2019 logs
-rwxrwxrwx 1 root root  227 Jun 27  2019 version.txt
[root@node3 mycat]# echo 'export PATH=/app/mycat/bin:$PATH' >> /etc/profile
[root@node3 mycat]# source /etc/profile

5.4配置文件介绍

 
logs目录:
wrapper.log       ---->mycat启动日志
mycat.log         ---->mycat详细工作日志
conf目录:
schema.xml      
主配置文件(读写分离、高可用、分布式策略定制、节点控制)
server.xml
mycat软件本身相关的配置
rule.xml 
分片规则配置文件,记录分片规则列表、使用方法等

5.5启动和连接

启动mycat

 
[root@node3 mycat]# mycat start
Starting Mycat-server...
[root@node3 mycat]# ps -ef |grep mycat
root       48553       1  0 05:29 ?        00:00:00 /app/mycat/bin/./wrapper-linux-x86-64 /app/mycat/conf/wrapper.conf wrapper.syslog.ident=mycat wrapper.pidfile=/app/mycat/logs/mycat.pid wrapper.daemonize=TRUE wrapper.lockfile=/var/lock/subsys/mycat
root       48555   48553 12 05:29 ?        00:00:02 java -DMYCAT_HOME=. -server -XX:MaxPermSize=64M -XX:+AggressiveOpts -XX:MaxDirectMemorySize=2G -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1984 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Xmx4G -Xms1G -Djava.library.path=lib -classpath lib/wrapper.jar:conf:lib/asm-4.0.jar:lib/commons-collections-3.2.1.jar:lib/commons-lang-2.6.jar:lib/curator-client-2.11.0.jar:lib/curator-framework-2.11.0.jar:lib/curator-recipes-2.11.0.jar:lib/disruptor-3.3.4.jar:lib/dom4j-1.6.1.jar:lib/druid-1.0.26.jar:lib/ehcache-core-2.6.11.jar:lib/fastjson-1.2.12.jar:lib/guava-19.0.jar:lib/hamcrest-core-1.3.jar:lib/hamcrest-library-1.3.jar:lib/jline-0.9.94.jar:lib/joda-time-2.9.3.jar:lib/jsr305-2.0.3.jar:lib/kryo-2.10.jar:lib/leveldb-0.7.jar:lib/leveldb-api-0.7.jar:lib/libwrapper-linux-ppc-64.so:lib/libwrapper-linux-x86-32.so:lib/libwrapper-linux-x86-64.so:lib/log4j-1.2-api-2.5.jar:lib/log4j-1.2.17.jar:lib/log4j-api-2.5.jar:lib/log4j-core-2.5.jar:lib/log4j-slf4j-impl-2.5.jar:lib/mapdb-1.0.7.jar:lib/minlog-1.2.jar:lib/mongo-java-driver-2.11.4.jar:lib/Mycat-server-1.6.7.1-release.jar:lib/mysql-binlog-connector-java-0.16.1.jar:lib/mysql-connector-java-5.1.35.jar:lib/netty-3.7.0.Final.jar:lib/netty-buffer-4.1.9.Final.jar:lib/netty-common-4.1.9.Final.jar:lib/objenesis-1.2.jar:lib/reflectasm-1.03.jar:lib/sequoiadb-driver-1.12.jar:lib/slf4j-api-1.6.1.jar:lib/univocity-parsers-2.2.1.jar:lib/velocity-1.7.jar:lib/wrapper.jar:lib/zookeeper-3.4.6.jar -Dwrapper.key=eTK9vQYBISYnKubv -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=48553 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.service=TRUE -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1 org.tanukisoftware.wrapper.WrapperSimpleApp io.mycat.MycatStartup start
root       48606   48307  0 05:29 pts/0    00:00:00 grep --color=auto mycat

链接mycat

[root@node3 app]# mysql -uroot -p123456 -h127.0.0.1 -P8066
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.29-mycat-1.6.7.1-release-20190627191042 MyCat Server (OpenCloudDB)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |
+----------+
1 row in set (0.02 sec)

#mycat默认的密码为123456,端口为8066

六、mycat测试

6.1测试环境准备

1、db01用户创建及测试数据库导入

因为主从关系,db01上的数据会自动复制到db02和其它主从关系的数据库上

 
以world.sql为测试数据库

db01:
mysql -S /data/3307/mysql.sock 
grant all on *.* to root@'192.168.32.%' identified by '123';
source /tmp/world.sql

mysql -S /data/3308/mysql.sock 
grant all on *.* to root@'192.168.32.%' identified by '123';
source /tmp/world.sql

2、mycat配置文件配置

node3

[root@node3 ~]#cd /app/mycat/conf
[root@node3 ~]#mv schema.xml schema.xml.bak
[root@node3 ~]#cat >> schema.xml <<EOF 
<?xml version="1.0"?>  
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">  
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> 
</schema>  
    <dataNode name="dn1" dataHost="ywx1" database= "wordpress" />  
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1"> 
        <heartbeat>select user()</heartbeat>  
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123"> 
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" /> 
    </writeHost> 
    </dataHost>  
</mycat:schema>
EOF

6.2mycat配置文件说明

1、定义逻辑库名称及数据节点名称

 
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> 
</schema>  

#name="TESTDB"为配置的逻辑库名称,dataNode="dn1"为数据节点名称

2、 定义数据节点:datanode和关联后端真实数据库

<dataNode name="dn1" dataHost="ywx1" database= "world" />  

#定义数据主机名称dataHost="ywx1",和后端真实数据库database="world"

3、数据主机:datahost(w写和r读)

 
<dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1"> 
        <heartbeat>select user()</heartbeat>  
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123"> 
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" /> 
    </writeHost> 
    </dataHost>  
    
  #db1数据主机192.168.32.201:3307为写库
  #db2数据主机192.168.32.202:3309为读库

6.3配置中的属性参数介绍

1、balance属性(默认即可)

负载均衡类型,目前的取值有3种: 
1. balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。 
2. balance="1",全部的readHost与standby writeHost参与select语句的负载均衡,简单的说,
  当双主双从模式(M1->S1,M2->S2,并且M1与 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。 
3. balance="2",所有读操作都随机的在writeHost、readhost上分发。

2、writeType属性(默认即可)

负载均衡类型,目前的取值有2种: 
1. writeType="0", 所有写操作发送到配置的第一个writeHost,
第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为主,切换记录在配置文件中:dnindex.properties . 
2. writeType=“1”,所有写操作都随机的发送到配置的writeHost,不建议使用

3、switchType属性(默认即可)

-1 表示不自动切换 
1 默认值,自动切换 
2 基于MySQL主从同步的状态决定是否切换 ,心跳语句为 show slave status 

4、datahost其他配置

<dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1"> 

maxCon="1000":最大的并发连接数
minCon="10" :mycat在启动之后,会在后端节点上自动开启的连接线程数
tempReadHostAvailable="1"
这个一主一从时(1个writehost,1个readhost时),可以开启这个参数,如果2个writehost,2个readhost时
<heartbeat>select user()</heartbeat>  监测心跳

七、mycat的读写分离配置

 
node3

[root@node3 ~]# cd /app/mycat/conf/
[root@node3 ~]#vim schema.xml 

<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">  
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> 
</schema>  
        <dataNode name="dn1" dataHost="ywx1" database= "world" />         
        <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">    
                <heartbeat>select user()</heartbeat>  
        <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123"> 
                        <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" /> 
        </writeHost> 
        </dataHost>  
</mycat:schema>

重启mycat
[root@node3 ~]#mycat restart

读写分离测试
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 

#查询操作为192.168.32.201:3309,server_id=9
 mysql> show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 9     |
+---------------+-------+
1 row in set (0.02 sec)

Query OK, 0 rows affected (0.01 sec)

#模拟写操作为192.168.32.201:3307,server_id=7
 
 mysql> begin;show variables like 'server_id';commit;

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 7     |
+---------------+-------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

以上案例实现了1主1从的读写分离功能,写操作落到主库,读操作落到从库.如果主库宕机,从库不能在继续提供服务了。

 
#关闭主库192.168.32.201:3307
#db01
[root@db01 tmp]# systemctl stop mysqld3307
[root@db01 tmp]# ss -antlp|grep 33
LISTEN     0      80          :::3308                    :::*                   users:(("mysqld",pid=18329,fd=22))
LISTEN     0      80          :::3309                    :::*                   users:(("mysqld",pid=18364,fd=22))
LISTEN     0      80          :::3310   

#在mycat node3上测试查询信息
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 
mysql> show variables like 'server_id';
ERROR 1184 (HY000): java.net.ConnectException: Connection refused
mysql> 
#无法连接数据库

#重启主库192.168.32.201:3307
#db01
[root@db01 tmp]# systemctl start mysqld3307

#在mycat上执行读写操作
#node3
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 
mysql>  show variables like 'server_id';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 9     |
+---------------+-------+
1 row in set (0.01 sec)

mysql> begin; show variables like 'server_id';commit;
Query OK, 0 rows affected (0.00 sec)

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 7     |
+---------------+-------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.00 sec)
#读写恢复正常
#验证了如果主库宕机,从库不能在继续提供服务了。

八、配置读写分离及高可用

为了解决主库宕机,从库不能在继续提供服务了。我们引入standby writeHost数据库。

我们实验里面使用db02:192.168.32.202:3307

前提是db01=192.168.32.201:3307与db02=192.168.32.202:3307互为主从

mycat的配置

node3

[root@node3 conf]# cat /app/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="false" sqlMaxLimit="100" dataNode="dn1"> 
</schema>  
    <dataNode name="dn1" dataHost="ywx1" database= "world" />  
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1"> 
        <heartbeat>select user()</heartbeat>  
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123"> 
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" /> 
    </writeHost> 
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123"> 
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" /> 
    </writeHost>        
    </dataHost>  
</mycat:schema>

#<writeHost host="db3" url="192.168.32.202:3307" user="root" password="123"> 
#            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" /> 
#    </writeHost>
#定义的standby  writeHost;其中writeHost=192.168.32.202:3307   readHost=192.168.32.202:3309
#其中配置文件中定义的writehost db1=192.168.32.201:3307与writehost db1=192.168.32.202:3307互为主从

测试

 
[root@node3 conf]# mycat restart
Stopping Mycat-server...
Stopped Mycat-server.
Starting Mycat-server...

[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 
#写操作
mysql> begin;select @@server_id;commit;
Query OK, 0 rows affected (0.00 sec)

+-------------+
| @@server_id |
+-------------+
|           7 |
+-------------+
1 row in set (0.06 sec)

Query OK, 0 rows affected (0.00 sec)

#读操作
mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          19 |
+-------------+
1 row in set (0.00 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          17 |
+-------------+
1 row in set (0.00 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|           9 |
+-------------+
1 row in set (0.00 sec)
#读操作为分布在192.168.32.201:3309(server_id=9),192.168.32.202:3307(server_id=17),192.168.32.202:3309(server_id=19)上。
#因为在主库为宕机前
#真正的 writehost=db1(192.168.32.201:3307):负责写操作的writehost  
#standby  writeHost=db3(192.168.32.202:3307)  :和readhost一样,只提供读服务

假设主库192.168.32.201:3307宕机,则写操作为落在192.168.32.202:3307上,读操作只有192.168.32.202:3309

#db01
[root@db01 tmp]# systemctl stop mysqld3307

#node3
#mycat测试
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 
#读操作只会落在192.168.32.202:3309(server_id=19)
mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          19 |
+-------------+
1 row in set (0.00 sec)

#写操作落在192.168.32.202:3307(server_id=17)上
mysql> begin; show variables like 'server_id';commit;
Query OK, 0 rows affected (0.00 sec)

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 17    |
+---------------+-------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

192.168.32.201:3307主库恢复,则它自动成为standby writeHost,写还是192.168.32.202:3307(server_id=17)上;

读为分布在192.168.32.202:3309(server_id=19),192.168.32.201:3307(server_id=7),192.168.32.201:3309(server_id=9)上

 
#db01
[root@db01 tmp]# systemctl start mysqld3307

#node3
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password:
#写操作
mysql> begin; show variables like 'server_id';commit;
Query OK, 0 rows affected (0.00 sec)

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id     | 17    |
+---------------+-------+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

#读操作
mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|          19 |
+-------------+
1 row in set (0.00 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|           9 |
+-------------+
1 row in set (0.00 sec)

mysql> select @@server_id;
+-------------+
| @@server_id |
+-------------+
|           7 |
+-------------+
1 row in set (0.00 sec)

九、垂直分表

9.1垂直分表的介绍

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面。

系统被切分成了,用户,订单交易,支付几个模块。 一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的 数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统 的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。 但是往往系统之有些表难以做到完全的独立,存在这扩库 join 的情况,对于这类的表,就需要去做平衡, 是数据库让步业务,共用一个数据源,还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较 少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到了一定的规模,负载很大的情况,就需要必 须去做分割。 一般来讲业务存在着复杂 join 的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种 程度是考验技术架构的一个难题。

下垂直切分的优缺点:

优点:

• 拆分后业务清晰,拆分规则明确; 

• 系统之间整合或扩展容易

• 数据维护简单。

缺点:

 • 部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度; 

• 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高; 

• 事务处理复杂

9.2垂直分表配置说明

垂直分表的配置文件

node3
[root@node3 conf]#cd /app/mycat/conf
[root@node3 conf]#vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="user" dataNode="sh1"/>
        <table name="order_t" dataNode="sh2"/>
</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>

9.3配置文件说明

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="user" dataNode="sh1"/>
        <table name="order_t" dataNode="sh2"/>

把user表放在sh1数据节点上,order_t表放在sh2数据节点上,其它表默认放在sh1数据节点

 

</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />

定义数据节点sh1和sh2的数据主机名称为ywx1和ywx2,并关联后端的真实数据库taobao

 

<dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
#定义数据主机ywx1的读写信息    
    
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
#定义数据主机ywx2的读写信息    

9.4测试

创建测试库和表:
#db01
[root@db01 conf]# mysql -S /data/3307/mysql.sock -e "create database taobao charset utf8;"
[root@db01 conf]# mysql -S /data/3308/mysql.sock -e "create database taobao charset utf8;"
[root@db01 conf]# mysql -S /data/3307/mysql.sock -e "use taobao;create table user(id int,name varchar(20))";
[root@db01 conf]# mysql -S /data/3308/mysql.sock -e "use taobao;create table order_t(id int,name varchar(20))"

#在mycat上查看
#node3
[root@node3 conf]# mycat restart
Stopping Mycat-server...
Stopped Mycat-server.
Starting Mycat-server...
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P8066
Enter password: 
mysql> show databases;
+----------+
| DATABASE |
+----------+
| TESTDB   |
+----------+
1 row in set (0.00 sec)

mysql> use TESTDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+------------------+
| Tables_in_taobao |
+------------------+
| order_t          |
| user             |
+------------------+
2 rows in set (0.01 sec)
#在mycat可以看到所有taobao库中所有表的信息

#在sh1上只能看到order_t的表的信息
#db01 192.168.32.201:3307
[root@db01 tmp]# mysql -S /data/3307/mysql.sock -e "use taobao;show tables;"
+------------------+
| Tables_in_taobao |
+------------------+
| user             |
+------------------+

#在sh2上只能看到order_t 的表的信息
#db01 192.168.32.201:3308
[root@db01 tmp]# mysql -S /data/3308/mysql.sock -e "use taobao;show tables;"
+------------------+
| Tables_in_taobao |
+------------------+
| order_t          |
+------------------+

十、MyCAT核心特性——分片(水平拆分)

10.1、水平拆分介绍

由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶 颈,所以就需要水平拆分来做解决。

相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中 包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分 到一个数据库,而另外的某些行又切分到其他的数据库中。

 

拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如: 从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分, 不同的数据按照会员 ID 做分组,这样所有的数据查询 join 都会在单库内解决;如果从商户的角度来讲,要查询某 个商家某天所有的订单数,就需要按照商户 ID 做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有 一定的困难。如何找到合适的分片规则需要综合考虑衡量。

几种典型的分片规则包括:

• 按照用户 ID 求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中;
• 按照日期,将不同月甚至日的数据分散到不同的库中;
• 按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中。

切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户 ID 求模举例:

 

既然数据做了拆分有优点也就优缺点。

优点:

• 拆分规则抽象好,join 操作基本可以数据库做;
• 不存在单库大数据,高并发的性能瓶颈;
• 应用端改造较少;
• 提高了系统的稳定性跟负载能力。

缺点:

• 拆分规则难以抽象;
• 分片事务一致性难以解决;
• 数据多次扩展难度跟维护量极大;
• 跨库 join 性能较差。

前面讲了垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同的特点缺点有:

• 引入分布式事务的问题;
• 跨节点 Join 的问题;
• 跨节点合并排序分页问题;
• 多数据源管理问题。

针对数据源管理,目前主要有两种思路:

A. 客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库, 在模块内完成数据的整合;

B. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;

可能 90%以上的人在面对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂 的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的 扩展性来说,是非常有帮助的。

Mycat 通过数据切分解决传统数据库的缺陷,又有了 NoSQL 易于扩展的优点。通过中间代理层规避了多数 据源的处理问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。

由于数据切分后数据 Join 的难度在此也分享一下数据切分的经验:

第一原则:能不切分尽量不要切分。

第二原则:如果要切分一定要选择合适的切分规则,提前规划好。

第三原则:数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能。 第四原则:由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量 少使用多表 Join。

10.2水平拆分的作用

分片:对一个"bigtable",比如说t3表

(1)行数非常多,800w
(2)访问非常频繁

分片的目的:
(1)将大数据量进行分布存储
(2)提供均衡的访问路由

10.3分片的策略

分片策略:
范围 range  800w  1-400w 400w01-800w
取模 mod    取余数
枚举 
哈希 hash 
时间 流水

优化关联查询
全局表
ER分片

1、范围分片range

比如说t3表

(1)行数非常多,2000w(1-1000w:sh1 1000w01-2000w:sh2)

(2)访问非常频繁,用户访问较离散

范围分片mycat配置文件

node3

[root@node3 logs]# cd /app/mycat/conf
[root@node3 conf]# vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1"> 
        <table name="t3" dataNode="sh1,sh2" rule="auto-sharding-long" />
</schema>  
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" /> 
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />  
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>


####################################################################
#<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1"> 
#       <table name="t3" dataNode="sh1,sh2" rule="auto-sharding-long" />
#把t3表按照范围分片的方式分配到sh1和sh2数据节点上;其它表默认在sh1分片上。
#rule="auto-sharding-long"   auto-sharding-long为range规则
    

在rule.xml配置文件中查看range的规则定义

 
node3

[root@node3 conf]#cd /app/mycat/conf
[root@node3 conf]# vim rule.xm
<tableRule name="auto-sharding-long">
                <rule>
                        <columns>id</columns>
                        <algorithm>rang-long</algorithm>
                </rule>             
<function name="rang-long"
    class="io.mycat.route.function.AutoPartitionByLong">
    <property name="mapFile">autopartition-long.txt</property>
</function>

#<columns>id</columns> 定义以id列来分片
#<algorithm>rang-long</algorithm> 调用rang-log函数
#<function name="rang-long" 查看rang-log函数信息
#    class="io.mycat.route.function.AutoPartitionByLong">
#    <property name="mapFile">autopartition-long.txt</property>
#</function>
#rang-log函数信息关联autopartition-long.txt脚本,没有可以自行创建在conf目录中

配置autopartition-long.txt函数

 
[root@node3 conf]# vim autopartition-long.txt 
# range start-end ,data node index
# K=1000,M=10000.
#0-500M=0
#500M-1000M=1
#1000M-1500M=2
#默认配合
0-10=0
11-20=1
#0表示sh1的分片
#1表示sh2的分片
#id:0-10被分配到sh1的分片上
#id:11-20被分配到sh1的分片上

测试

db01
创建测试表:
mysql -S /data/3307/mysql.sock -e "use taobao;create table t3 (id int not null primary key auto_increment,name varchar(20) not null);"

mysql -S /data/3308/mysql.sock  -e "use taobao;create table t3 (id int not null primary key auto_increment,name varchar(20) not null);"


重启mycat
mycat restart
在mycat上插入数据
[root@node3 conf]# mysql -uroot -p -h 127.0.0.1 -P 8066
password:
mysql> use TESTDB;
mysql> insert into t3(id,name) values(1,'a');
mysql> insert into t3(id,name) values(2,'b');
mysql> insert into t3(id,name) values(3,'c');
mysql> insert into t3(id,name) values(4,'d');
mysql> insert into t3(id,name) values(11,'aa');
mysql> insert into t3(id,name) values(12,'bb');
mysql> insert into t3(id,name) values(13,'cc');
mysql> insert into t3(id,name) values(14,'dd');
mysql> select * from t3;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
|  4 | d    |
| 11 | aa   |
| 12 | bb   |
| 13 | cc   |
| 14 | dd   |
+----+------+
8 rows in set (0.05 sec)
#在mycat上看到的是全表的数据

#db02
#在sh1上查看t3表信息,只能看到id 1-4的信息
192.168.32.202:3307
[root@db02 ~]# mysql -S /data/3307/mysql.sock  -e "use taobao;select * from t3;"
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
|  4 | d    |
+----+------+
#在sh2上查看t3表信息,只能看到id 11-14的信息
192.168.32.202:3308
[root@db02 ~]# mysql -S /data/3308/mysql.sock  -e "use taobao;select * from t3;"
+----+------+
| id | name |
+----+------+
| 11 | aa   |
| 12 | bb   |
| 13 | cc   |
| 14 | dd   |
+----+------+

2、取模分片(mod-long)

取余分片方式:分片键(一个列)与节点数量进行取余,得到余数,将数据写入对应节点。

mycat的配置文件

node3

[root@node3 conf]#cd /app/mycat/conf
[root@node3 conf]# vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="t4" dataNode="sh1,sh2" rule="mod-long" />
</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>

#<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
#        <table name="t4" dataNode="sh1,sh2" rule="mod-log" />
#把t4按照取模的方式分片到sh1,sh2
#其它表默认放在sh1上
#mod-log表示取模规则

在rule.xml配置文件中查看mod-long的规则定义

[root@node3 conf]# vim rule.xml
<tableRule name="mod-long">
      <rule>
          <columns>id</columns>
          <algorithm>mod-long</algorithm>
      </rule>
</tableRule>

#<columns>id</columns> 按照id列来取模
#<algorithm>mod-long</algorithm> 调用mod-long函数


<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
             <!-- how many data nodes -->
             <property name="count">2</property>
</function>

#<property name="count">2</property> 表示有多少数据节点,我们这里只有sh1和sh2两个,填写2
#columns 标识将要分片的表字段,algorithm 分片函数, 其中分片函数配置中,mapFile标识配置文件名称

测试

#创建测试表:
#db02

[root@db02 ~]#mysql -S /data/3307/mysql.sock -e "use taobao;create table t4 (id int not null primary key auto_increment,name varchar(20) not null);"
[root@db02 ~]#mysql -S /data/3308/mysql.sock -e "use taobao;create table t4 (id int not null primary key auto_increment,name varchar(20) not null);"

#重启mycat 
#node3

[root@node3 ~]#mycat restart 

测试: 
[root@node3 ~]#mysql -uroot -p123456 -h127.0.0.1 -P8066

mysql> use TESTDB
mysql> insert into t4(id,name) values(1,'a');
mysql> insert into t4(id,name) values(2,'b');
mysql> insert into t4(id,name) values(3,'c');
mysql> insert into t4(id,name) values(4,'d');

mysql> select * from t4;
+----+------+
| id | name |
+----+------+
|  2 | b    |
|  4 | d    |
|  1 | a    |
|  3 | c    |
+----+------+
4 rows in set (0.10 sec)


#分别登录后端节点查询数据
#db02

[root@db02 ~]# mysql -S /data/3308/mysql.sock -e "use taobao;select * from t4;"
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  3 | c    |
+----+------+
[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "use taobao;select * from t4;"
+----+------+
| id | name |
+----+------+
|  2 | b    |
|  4 | d    |
+----+------+

3、枚举分片

 
t5 表
id name telnum
1   bj   1212
2   sh   22222
3   bj   3333
4   sh   44444
5   bj   5555

mycat配置文件

 
[root@node3 ~]# cd /app/mycat/conf
[root@node3 conf]# vim schema.xml
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="t5" dataNode="sh1,sh2" rule="sharding-by-intfile" />
</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>


#<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="sh1">
#        <table name="t5" dataNode="sh1,sh2" rule="sharding-by-intfile" />
#把t5表按照枚举的方式分布到sh1和sh2上,其它表默认在sh1上
# rule="sharding-by-intfile"  枚举规则

在rule.xml配置文件中查看sharding-by-intfile的规则定义

 
<tableRule name="sharding-by-intfile">
                <rule>
                        <columns>name</columns>
                        <algorithm>hash-int</algorithm>
                </rule>

# <columns>name</columns> 以那一列来进行枚举,这里使用name列
#<algorithm>hash-int</algorithm> 调用hash-int函数

<function name="hash-int"
                class="io.mycat.route.function.PartitionByFileMap">
                <property name="mapFile">partition-hash-int.txt</property>
                    <property name="type">1</property>
                      <property name="defaultNode">0</property>
</function>
#hash-int函数调用partition-hash-int.txt配置文件
#<property name="type">1</property>
#<property name="defaultNode">0</property>
#手动添加以上2行,支持中文字符
#columns 标识将要分片的表字段,algorithm 分片函数, 其中分片函数配置中,mapFile标识配置文件名称

partition-hash-int.txt配置文件,没有在mycat conf目录中创建

默认为2进制
[root@node3 conf]# vim partition-hash-int.txt 
10000=0
10010=1
修改为
[root@node3 conf]# vim partition-hash-int.txt
bj=0 
sh=1
DEFAULT_NODE=1 
#0表示分片sh1
#1表示分片sh2
#把name=bj放在sh1
#把name=sh放在sh2
#其它的放在默认分片sh1上

测试

#准备测试环境
#db02
[root@db02 ~]#mysql -S /data/3307/mysql.sock -e "use taobao;create table t5 (id int not null primary key auto_increment,name varchar(20) not null);"

[root@db02 ~]#mysql -S /data/3308/mysql.sock -e "use taobao;create table t5 (id int not null primary key auto_increment,name varchar(20) not null);"


#重启mycat 
#node3
[root@node3 ~]#mycat restart 
[root@node3 ~]#mysql -uroot -p123456 -h127.0.0.1 -P8066
mysql>use TESTDB
mysql>insert into t5(id,name) values(1,'bj');
mysql>insert into t5(id,name) values(2,'sh');
mysql>insert into t5(id,name) values(3,'bj');
mysql>insert into t5(id,name) values(4,'sh');
mysql>insert into t5(id,name) values(5,'tj');
mysql> select * from t5;
+----+------+
| id | name |
+----+------+
|  1 | bj   |
|  3 | bj   |
|  5 | tj   |
|  2 | sh   |
|  4 | sh   |
+----+------+
5 rows in set (0.10 sec)


#分别登录后端节点查询数据
#db02

[root@db02 ~]# mysql -S /data/3308/mysql.sock -e "use taobao;select * from t5;"
+----+------+
| id | name |
+----+------+
|  2 | sh   |
|  4 | sh   |
+----+------+
[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "use taobao;select * from t5;"
+----+------+
| id | name |
+----+------+
|  1 | bj   |
|  3 | bj   |
|  5 | tj   |
+----+------+

#tj放在默认的sh1分片上的

4、Mycat全局表

 
a   b   c  d   
join 
t 
#表a、b、c、d由于表t有关联
select  t1.name   ,t.x  from  t1 
join t 
select  t2.name   ,t.x  from  t2 
join t 
select  t3.name   ,t.x  from  t3 
join t 

使用场景:
如果你的业务中有些数据类似于数据字典,比如配置文件的配置,
常用业务的配置或者数据量不大很少变动的表,这些表往往不是特别大,
而且大部分的业务场景都会用到,那么这种表适合于Mycat全局表,无须对数据进行切分,
要在所有的分片上保存一份数据即可,Mycat 在Join操作中,业务表与全局表进行Join聚合会优先选择相同分片内的全局表join,
避免跨库Join,在进行数据插入操作时,mycat将把数据分发到全局表对应的所有分片执行,在进行数据读取时候将会随机获取一个节点读取数据。 

mycat配置文件

[root@node3 conf]# vim /app/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="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="t_area" primaryKey="id"  type="global" dataNode="sh1,sh2" /> 
</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>

# <table name="t_area" primaryKey="id"  type="global" dataNode="sh1,sh2" />
#把t_area表,主键列为id,在分片sh1和sh2上都设置为全局表

测试

#后端数据准备
#db02
[root@db02 ~]# mysql -S /data/3307/mysql.sock -e "use taobao;create table t_area (id int not null primary key auto_increment,name varchar(20) not null);"
[root@db02 ~]# mysql -S /data/3308/mysql.sock -e "use taobao;create table t_area (id int not null primary key auto_increment,name varchar(20) not null);"




#重启mycat 
#node3
[root@node3 conf]#mycat restart 

测试: 
[root@node3 conf]#mysql -uroot -p123456 -h127.0.0.1 -P8066

mysql > use TESTDB
mysql > insert into t_area(id,name) values(1,'a');
mysql > insert into t_area(id,name) values(2,'b');
mysql > insert into t_area(id,name) values(3,'c');
mysql > insert into t_area(id,name) values(4,'d');

5、E-R分片

 
A 
join 
B  
为了防止跨分片join,可以使用E-R模式
A   join   B
on  A.xx=B.yy
join C
on A.id=C.id

<table name="A" dataNode="sh1,sh2" rule="mod-long"> 
       <childTable name="B" joinKey="yy" parentKey="xx" /> 
       <childTable name="C" joinKey="id" parentKey="id" />
</table> 
#<table name="A" dataNode="sh1,sh2" rule="mod-long"> 父表A信息
#<childTable name="B" joinKey="yy" parentKey="xx" /> 子表B的信息,关联关系: A.xx=B.yy
#<childTable name="C" joinKey="id" parentKey="id" /> 子表C的信息,关联关系: A.id=C.id

mycat配置

 
[root@node3 conf]# vim /app/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="false" sqlMaxLimit="100" dataNode="sh1">
        <table name="A" dataNode="sh1,sh2" rule="mod-long"> 
           <childTable name="B" joinKey="yy" parentKey="xx" /> 
           <childTable name="C" joinKey="id" parentKey="id" />
</schema>
    <dataNode name="sh1" dataHost="ywx1" database= "taobao" />
    <dataNode name="sh2" dataHost="ywx2" database= "taobao" />
    <dataHost name="ywx1" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3307" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3309" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3307" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3309" user="root" password="123" />
    </writeHost>
    </dataHost>
    <dataHost name="ywx2" maxCon="1000" minCon="10" balance="1"  writeType="0" dbType="mysql"  dbDriver="native" switchType="1">
        <heartbeat>select user()</heartbeat>
    <writeHost host="db1" url="192.168.32.201:3308" user="root" password="123">
            <readHost host="db2" url="192.168.32.201:3310" user="root" password="123" />
    </writeHost>
    <writeHost host="db3" url="192.168.32.202:3308" user="root" password="123">
            <readHost host="db4" url="192.168.32.202:3310" user="root" password="123" />
    </writeHost>
    </dataHost>
</mycat:schema>

 

posted @ 2020-12-09 19:03  yaowx  阅读(216)  评论(0编辑  收藏  举报