1. 程式人生 > 實用技巧 >二十三、Mysql讀寫分離之Mycat

二十三、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>