MyCAT读写分离分库分表

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyCAT读写分离分库分表相关的知识,希望对你有一定的参考价值。

MyCAT读写分离及分库分表

第1章 MyCAT分布式系统解决方案

1.1 分布式系统介绍:

分布式系统特性:

1.      透明性:

a)        分布式系统对用户来说是透明的,一个分布式系统在用户面前的表现就像一个传统的单机处理机分时系统,可以让用户不比了解内部结构就可以使用

2.      扩展性:

a)        分布式系统的最大特点就是扩展性,它可以分局需求的增加而扩展,可以通过横向扩展使集群的整体性能得到线性提升,也可以通过纵向扩展单台服务器的性能使服务器的集群的性能得到提升

1.2 MyCAT的设计理念:

mycat的原理中最重要的一个动词就是拦截,它拦截了用户发送过来的sql语句,首先对sql语句组了一些特定的分析,如分片分析,路由分析,读写分析,分离分析,缓存分析,然后将此sql语句发往后端的真实数据库,并将返回结果做适当处理,最终返回给用户

技术分享图片

1.1 MyCAT软件特点:

1.      遵守mysql原生协议,跨语言,跨数据库的通用中间件代理

2.      基于心跳的自动故障切换,支持读写分离,支持mysql一双主,多从,以及一主多从

3.      有效管理数据源连接,基于数据分库,而不是分表的模式

4.      基于Nio实现,有效管理线程,高并发问题

5.      支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数

6.      支持2join,甚至基于caltet的多表join

7.      支持通过全局表,ER关系的分片策略,实现了高效的夺标join查询

8.      支持多租户方案

9.      支持分布式事务

10.  支持全局序列号,解决分布式下的主键生成问题

11.  分片规则丰富,插件化开发,易于扩展

12.  强大的web,命令行监控

13.  支持前端作为mysql通用代理,后端JDBC方式支持Oracle,DB2,SQL server,MongoDB

14.  集群基于zookeeper管理,在线升级,扩容,智能优化,大数据处理

1.2 MyCAT典型应用场景:

1.      典型的读写分离,此时配置最为简单,支持读写分离,主从切换

2.      分库分表,对于超过1000万的表进行切分,最大支持1000亿级别的单表分片

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

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

1.3 数据库的切分方式:

何为数据(系统)切分?

简单来说,就是通过某种特定条件,按照某个维度(或库或表级别)将我们存放在同一个数据库中的数据分散存放到多个数据库主机上以达到分散单库(主机)负载的效果

1.3.1 切分模式:

1.3.2 垂直切分:

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

a)      优点:拆分后业务清晰,拆分规则明确,数据维护简单,系统之间整合或者扩展容易

b)      缺点:部分业务表无法join,只能通过接口方式解决,提高了系统复杂度,受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高,事务处理复杂

技术分享图片

1.1.1 水平切分:

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

a)      优点:不存在单库大数据,高并发的性能瓶颈,对应用透明,应用端改造较少,按照合理拆分规则拆分,join操作基本避免跨库,提高了系统的稳定性跟负载能力

b)      缺点:拆分规则难以抽象,分片事务一致性难以解决,数据多次扩展难度跟维护量极大,跨库join性能较差

技术分享图片

1.1.1 切分的处理难点:

无论哪种切分方式,他们存在的共同缺点有:引入分布事务的问题,跨节点的join问题,跨节点合并排序分页的问题

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

1.      客户端模式,在每个应用程序模块中配置管理自己需要的一个或者多个数据源,直接访问各个数据库,在模块内完成数据的整合,优点是相对简单,没有性能的损耗

2.      通过中间代理层来统一管理所有数据源,后端数据库集群对前端应用程序透明,优点是比较通用,改造少

1.1.3 切分原则:

1.      尽量不切分,架构是进化而来,数据量也是慢慢增加上来,不是一蹴而就

2.      最大可能的找到最合适的切分难度

3.      由于数据库中间件对数据join实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表join

4.      尽量通过数据冗余,分组避免数据跨库的多表join

5.      尽量避免分布式事务

6.      单表切分数据最好在1000万行以内

for i in {6..9};do mysqladmin -S /data/530${i}/mysql.sock shutdown ;done

for i in {6..9};do mysqld_safe --defaults-file=/data/530${i}/my.cnf --pid-file=/data/530${i}/530${i}.pid  & >/dev/null 2>&1 ; done

第2章 Mycat架构说明:

2.1 基本架构:

标准的读写分离主从模式,主节点负责写操作,从节点负责读操作

技术分享图片

1.1 mycat服务器配置高可用:

haproxy+mycat,应用简单,又可以避免mycat服务器的单间故障

技术分享图片

1.1 多节点分布式集群:

增加集群中的读节点,进行负载,分担数据库压力,同时,解决mysql高可用的问题,实现故障自动切换;

我们今天配置的就是两套这样的集群

技术分享图片

第1章 MyCAT基础架构:

1.1 环境准备:

db01 :

mysql多实例 : 5306-5307-5308-5309

db02:

       mysql多实例 : 6306-6307-6308-6309

说明:相当于一共八台mysql服务器

1.1.1 所有mysql服务器hosts文件进行解析,不然启动mycat时会报错

cat /etc/hosts

127.0.0.1 db02  localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

db01 10.0.0.51

db02 10.0.0.52

db03 10.0.0.53

说明:虚拟机资源限制,每台机器共四个实例,其中,两个节点互为主从

1.1.2 所有实例的配置文件:除端口不同外其他均相同

[[email protected] 6306]# cat my.cnf

[mysqld]

basedir=/application/mysql

datadir=/data/6306

socket=/data/6306/mysql.sock

log-error=/data/6306/mysql.log

log_bin=/data/6306/mysql-bin

binlog_format=row

skip_name_resolve=1

server_id=6306

port=6306

log_slave_updates=1           强制生成从节点的中继日志

1.2 环境部署:

多实例的部署前面文档有,这里省略

1.2.1 db01db02初始化数据:

for i in {6..9};do /application/mysql/scripts/mysql_install_db --defaults-file=/data/530${i}/my.cnf --user=mysql --basedir=/application/mysql --datadir=/data/530${i} ; done

for i in {6..9};do /application/mysql/scripts/mysql_install_db --defaults-file=/data/630${i}/my.cnf --user=mysql --basedir=/application/mysql --datadir=/data/630${i} ; done

1.2.2 启动两台机器上的多实例:

for i in {6..9};do mysqld_safe --defaults-file=/data/530${i}/my.cnf --pid-file=/data/530${i}/530${i}.pid  & >/dev/null 2>&1 ;done

1.2.3 检查端口是否开启:

[[email protected] data]# ss -tunlp|grep 530

tcp    LISTEN     0      80       :::5306                 :::*                   users:(("mysqld",pid=4537,fd=11))

tcp    LISTEN     0      80       :::5307                 :::*                   users:(("mysqld",pid=4540,fd=11))

tcp    LISTEN     0      80       :::5308                 :::*                   users:(("mysqld",pid=4539,fd=11))

tcp    LISTEN     0      80       :::5309                 :::*                   users:(("mysqld",pid=4538,fd=11))

[[email protected] data]# ss -tunlp|grep 630

tcp    LISTEN     0      80       :::6306                 :::*                   users:(("mysqld",pid=4225,fd=11))

tcp    LISTEN     0      80       :::6307                 :::*                   users:(("mysqld",pid=4226,fd=11))

tcp    LISTEN     0      80       :::6308                 :::*                   users:(("mysqld",pid=4223,fd=11))

tcp    LISTEN     0      80       :::6309                 :::*                   users:(("mysqld",pid=4227,fd=11))

1.2.4 清除所有实例上的二进制日志:

for i in {6..9};do mysql -S /data/530${i}/mysql.sock -e 'reset master';done

for i in {6..9};do mysql -S /data/630${i}/mysql.sock -e 'reset master';done

1.2.5 检查日志是否刷新:

for i in {6..9};do mysql -S /data/530${i}/mysql.sock -e 'show master status';done

for i in {6..9};do mysql -S /data/630${i}/mysql.sock -e 'show master status';done

1.3 53066306的双主:

db01创建用户:

grant all on *.* to [email protected]'10.0.0.%' identified by '123';

db02操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.51',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=5306,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

db01操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.52',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=6306,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

db015306db026306双主构建完毕

1.4 构建5307指向5306的主从:

db01上操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.51',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=5306,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

1.5 构建6307指向6306的主从:

db02上操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.52',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=6306,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

1.6 构建db015308db026308双主:

db01操作:

grant all on *.* to [email protected]'10.0.0.%' identified by '123';

db02操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.51',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=5308,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

db01操作:

CHANGE MASTER TO

MASTER_HOST='10.0.0.52',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=6308,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

1.7 构建5309指向5308的主从:

CHANGE MASTER TO

MASTER_HOST='10.0.0.51',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=5308,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

1.8 构建6309指向6308的主从:

CHANGE MASTER TO

MASTER_HOST='10.0.0.52',

MASTER_USER='repl',

MASTER_PASSWORD='123',

MASTER_PORT=6308,

MASTER_LOG_FILE='mysql-bin.000001',

MASTER_LOG_POS=120;

start slave;

基本架构部署完毕

第2章 安装mycat:

2.1 环境准备:

安装java运行环境

yum -y install java

下载mycat软件:

http://dl.mycat.io/1.6-RELEASE

2.2 解压mycat:

tar xf Mycat-server-1.6.5-release-20180117003034-linux.tar.gz

2.3 启动服务:

[[email protected] bin]# ./mycat start

Starting Mycat-server...

ss -tunlp|grep 8066

tcp    LISTEN     0      100      :::8066                 :::*                   users:(("java",pid=6581,fd=79))

2.4 conf目录文件说明:

[[email protected] conf]# pwd

/server/tools/mycat/conf

-rwxrwxrwx 1 root root 4794 Jan 17 00:30 rule.xml

分片策略的定义配置

-rwxrwxrwx 1 root root 4219 Jan 17 00:30 schema.xml

逻辑库,高可用,读写分离,分片规则调用

-rwxrwxrwx 1 root root 4623 Jan 17 00:30 server.xml

mycat服务自身相关的配置信息

2.4.1 schema.xml配置文件说明:

<schema>

标签用于定义Mycat实例中的逻辑库,逻辑库的概念和mysql数据库中的database的概念相同

dataNode

dataNode标签定义了MyCat中的数据节点,用于绑定逻辑库到某个具体的database

<dataNode name="dn1" dataHost="lch3307" database="db1" ></dataNode>

dataNode标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。一个dataNode标签就是一个独立的数据分片。

例子中所表述的意思为:使用名字为lch3307数据库实例上的db1物理数据库,这就组成一个数据分片,最后,我们使用名字dn1

 checkSQLschema

当该值设置为true时,如果我们执行语句select * from TESTDB.travelrecord;MyCat会把语句修改为select * from travelrecord;。即把表示schema的字符去掉,避免发送到后端数据库执行时报(ERROR 1146 (42S02): Table ‘testdb.travelrecord’ doesn’t exist)。不过,即使设置该值为true ,如果语句所带的是并非是schema指定的名字,例如:select * from db1.travelrecord;那么MyCat并不会删除db1这个字段,如果没有定义该库的话则会报错,所以在提供SQL语句的最好是不带这个字段。

当设置为false时,则会把语句原封不动的发往最终的MySQL

SQLmaxlimit

当该schema中有分片表时,才会生效

当该值设置为某个数值时。每条执行的SQL语句,如果没有加上limit语句,MyCat也会自动的加上所对应的值。例如设置值为100,执行**select * from TESTDB.travelrecord;**的效果为和执行**select * from TESTDB.travelrecordlimit 100;**

table标签

<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" >

</table>

Table标签定义了MyCat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。

rule属性

该属性用于指定逻辑表要使用的规则名字,规则名字在rule.xml中定义,必须与tableRule标签中name属性属性值一一对应。

type属性

该属性定义了逻辑表的类型,目前逻辑表只有全局表普通表两种类型。对应的配置:

全局表:global

普通表:不指定该值为globla

name属性:

定义数据节点的名字,这个名字需要是唯一的,我们需要在table标签上使用这个名字,来建立表与分片的对应关系

dataHost属性

该属性用于定义该分片属于哪个数据库实例的,属性值是引用dataHost标签上定义的name属性

database属性

该属性用于定义该分片属性哪个具体数据库实例上的具体库,因为这里使用两个维度来定义分片,就是:实例+具体的库,因为每个库上建立的表和表结构是一样的,所以这样做就可以轻松的对表进行水平拆分

hearbeat标签

这个标签内指明用于和后端数据库进行心跳检查的语句,例如:mysql可以使用select user(),Oracle可以使用select 1 from test

writeHost标签,readHost标签

这两个标签都指定后端数据库的相关配置给mycat,用于实例化后端连接池,唯一不用的是,writeHost指定写实例,readHost指定读实例,在一个dataHost内可以定义多个writeHostreadHost,但是如果writeHost指定的后端数据库宕机,那么这个writeHost绑定的所有readHost都将不可用,另一方面,由于这个writeHost宕机系统会自动的检测到,并切换到备用的writeHost上去

host属性

用于标识不同实例

url属性

后端实例连接地址

user和password属性

mycat登录逻辑库的用户名和密码

weight属性

权重,配置在readHost中作为读节点的权重

balance属性

负载均衡类型,目前的取值有3种:

1. balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上。

2. balance="1",全部的readHoststand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1M2->S2,并且M1M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。

3. balance="2",所有读操作都随机的在writeHostreadhost

writeType属性

负载均衡类型,目前的取值有2种:

writeType="0", 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后的为准,切换记录在配置文件中:dnindex.properties.

2. writeType=“1”,所有写操作都随机的发送到配置的writeHost,但不

switchType属性

-1表示不自动切换

1 默认值,自动切换

2 基于mysql主从同步的状态决定是否切换

temReadHostAvaiable属性

如果配置了这个属性,writeHost下面的readHost仍旧可用,默认0, 1表示开启

2.4.2 server.xml配置文件说明:

server.xml中重点需要关注的标签是用于定义登录mycat的用户和权限

<user name="test">

<property name="password">test</property>

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

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

<property name="benchmark">11111</property>

<property name="usingDecrypt">1</property>

</user>

上面的例子中,我定义了一个用户,用户名为test、密码也为test可访问的schema也只有TESTDB一个。如果我在schema.xml中定义了多个schema,那么这个用户是无法访问其他的schema。在mysql客户端看来则是无法使用use切换到这个其他的数据库。

如果使用了use命令,则mycat会报出这样的错误提示:

ERROR 1044 (HY000): Access denied for user 'test' to database 'xxx'

Mysql连接相关属性

初始化mysql前后端连接所涉及到的一些属性:

packetHeaderSize: 指定Mysql协议中的报文头长度。默认4

maxPacketSize: 指定Mysql协议可以携带的数据最大长度。默认16M

idleTimeout: 指定连接的空闲超时时间。某连接在发起空闲检查下,发现距离上次使用超过了空闲时间,那么这个连接会被回收,就是被直接的关闭掉。默认30分钟,单位毫秒。

charset: 连接的初始化字符集。默认为utf8

txIsolation: 前端连接的初始化事务隔离级别,只在初始化的时候使用,后续会根据客户端传递过来的属性对后端数据库连接进行同步。默认为REPEATED_READ,设置值为数字默认3

READ_UNCOMMITTED = 1;

READ_COMMITTED = 2;

REPEATED_READ = 3;

SERIALIZABLE = 4;

sqlExecuteTimeout:SQL执行超时的时间Mycat会检查连接上最后一次执行SQL的时间,若超过这个时间则会直接关闭这连接。默认时间为300秒,单位秒。

服务相关的属性

bindIp: mycat服务监听的IP地址,默认值为0.0.0.0

serverPort: 定义mycat的使用端口,默认值为8066

managerPort: 定义mycat的管理端口,默认值为9066

2.4.3 rule.xml配置文件说明:

 

rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同。这个文件里面主要有tableRulefunction这两个标签。在具体使用过程中可以按照需求添加tableRulefunction

?tableRule标签

这个标签定义表规则。

定义的表规则,在schema.xml

<tableRulename="rule1">

<rule>

<columns>id</columns>

<algorithm>hash-int</algorithm>

</rule>

</tableRule>

name 属性指定唯一的名字,用于标识不同的表规则。

内嵌的rule标签则指定对物理表中的哪一列进行拆分和使用什么路由算法。

columns 内指定要拆分的列名字

algorithm 使用function标签中的name属性。连接表规则和具体路由算法。当然,多个表规则可以连接到同一个路由算法上。table标签内使用。让逻辑表使用这个规则进行

第3章 配置mycat读写分离:

3.1 案例一:简单读写分离配置

db01上创建用户:

grant all on *.* to [email protected]'10.0.0.%' identified by'123456';

3.1.1 修改schema配置文件:

[[email protected] 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="dn1">

</schema>

 

<dataNode name="dn1" dataHost="localhost1" database= "jiang" />

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

<heartbeat>select user()</heartbeat>

<writeHost host="db1" url="10.0.0.51:5306" user="root" password="123456">

<readHost host="db2" url="10.0.0.51:5307" user="root" password="123456" />

</writeHost>

</dataHost>

</mycat:schema>

重启mycat服务加载配置

[[email protected] conf]# ../bin/mycat  restart    

3.1.2 测试是否负载均衡:

[[email protected] mycat]# mysql -uroot -p123456 -h127.0.0.1 -P8066

mysql> show variables like '%server_id%';

+----------------+-------+

| Variable_name  | Value |

+----------------+-------+

| server_id      | 5307  |               #读节点为5307

| server_id_bits | 32    |

+----------------+-------+

2 rows in set (0.01 sec)

3.1.3 测试写节点是否为db1:

[[email protected] ~]# mysql -S /data/5307/mysql.sock

mysql> set global read_only=1;

[[email protected] mycat]# mysql -uroot -p123456 -h127.0.0.1 -P8066

mysql> use TESTDB;

mysql> create table test(id int);

Query OK, 0 rows affected (0.48 sec)       #写成功

3.2 案例二:四节点读写分离+高可用:

配置文件:

<?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="localhost1" database= "mysql" /> 

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

<heartbeat>select user()</heartbeat> 

<writeHost host="db1" url="10.0.0.51:5306" user="root" password="123456">

<readHost host="db2" url="10.0.0.51:5307" user="root" password="123456" />

</writeHost>

<writeHost host="db3" url="10.0.0.52:6306" user="root" password="123456">

<readHost host="db4" url="10.0.0.52:6307" user="root" password="123456" />

</writeHost>

</dataHost> 

</mycat:schema>

:以上配置中,只有db1复制写操作,其他三个节点负责读操作,db1宕机时,db3会接管db1写操作,db4负责读操作

3.2.1 测试负载均衡是否正常:

mysql> show variables like '%server_id%';

+----------------+-------+

| Variable_name  | Value |

+----------------+-------+

| server_id      | 5307  |

| server_id_bits | 32    |

+----------------+-------+

2 rows in set (0.01 sec)

 

mysql> show variables like '%server_id%';

+----------------+-------+

| Variable_name  | Value |

+----------------+-------+

| server_id      | 6307  |

| server_id_bits | 32    |

+----------------+-------+

2 rows in set (0.03 sec)

3.2.2 宕掉db1:

[[email protected] ~]# mysqladmin -S /data/5306/mysql.sock shutdown

3.2.3 测试写操作是否由db3执行:

[[email protected] 6306]# mysql -S /data/6307/mysql.sock         6307设置为只读,然后进行写操作

mysql> set global read_only=1;

mysql> insert into t1 values(1);

Query OK, 1 row affected (0.18 sec)

3.3 垂直分库

修改配置文件:

<?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> 

<schema name="mysql" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn2">

</schema>

    <dataNode name="dn1" dataHost="localhost1" database= "jiang" /> 

    <dataNode name="dn2" dataHost="localhost2" database= "mysql" /> 

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db1" url="10.0.0.51:5306" user="root" password="123456">

            <readHost host="db2" url="10.0.0.51:5307" user="root" password="123456" />

    </writeHost>

    <writeHost host="db3" url="10.0.0.52:6306" user="root" password="123456">

            <readHost host="db4" url="10.0.0.52:6307" user="root" password="123456" />

    </writeHost>

    </dataHost>

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db11" url="10.0.0.51:5308" user="root" password="123456">

            <readHost host="db22" url="10.0.0.51:5309" user="root" password="123456" />

    </writeHost>

    <writeHost host="db33" url="10.0.0.52:6308" user="root" password="123456">

            <readHost host="db44" url="10.0.0.52:6309" user="root" password="123456" />

    </writeHost>

    </dataHost>

</mycat:schema>

修改server.xml文件:

[[email protected] conf]# vim server.xml

        <user name="root" defaultAccount="true">

                <property name="password">123456</property>

                <property name="schemas">TESTDB,mysql</property>

重启服务:

[[email protected] conf]# ../bin/mycat  restart

检查数据库:

[[email protected] 6306]# mysql -uroot -p123456 -h127.0.0.1 -P8066

mysql> show databases;

+----------+

| DATABASE |

+----------+

| TESTDB   |

| mysql    |

+----------+

2 rows in set (0.03 sec)

3.4 垂直分表:

<?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">

    <table name="wp_users" dataNode="dn1"/>

    <table name="wp_usermeta" dataNode="dn2"/>

</schema> 

 

    <dataNode name="dn1" dataHost="localhost1" database= "jiang" />

    <dataNode name="dn2" dataHost="localhost2" database= "mysql" />

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db1" url="10.0.0.51:5306" user="root" password="123456">

            <readHost host="db2" url="10.0.0.51:5307" user="root" password="123456" />

    </writeHost>

    <writeHost host="db3" url="10.0.0.52:6306" user="root" password="123456">

            <readHost host="db4" url="10.0.0.52:6307" user="root" password="123456" />

    </writeHost>

    </dataHost>

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db11" url="10.0.0.51:5308" user="root" password="123456">

            <readHost host="db22" url="10.0.0.51:5309" user="root" password="123456" />

    </writeHost>

    <writeHost host="db33" url="10.0.0.52:6308" user="root" password="123456">

            <readHost host="db44" url="10.0.0.52:6309" user="root" password="123456" />

    </writeHost>

    </dataHost>

</mycat:schema>

修改server配置文件:

vim server.xml

                <user name="root" defaultAccount="true">

                <property name="password">123456</property>

                <property name="schemas">TESTDB,jiang</property>

3.5 分片设置:

1.      分片节点:

数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点

2.      节点主机:

数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器扇面可以有多个分片数据库,这样一个或多个分片节点,所在的机器就是界定啊主机(dataHost),为了规避节点主机并发数限制,尽量将读写压力高的分片节点均衡的放在不同的节点主机

 

<?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">

<table name="wp_usermeta" dataNode="dn1,dn2" rule="auto-sharding-long" >

</table>

</schema> 

 

    <dataNode name="dn1" dataHost="localhost1" database= "wordpress" />

    <dataNode name="dn2" dataHost="localhost2" database= "wordpress" />

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db1" url="10.0.0.51:5306" user="root" password="123456">

            <readHost host="db2" url="10.0.0.51:5307" user="root" password="123456" />

    </writeHost>

    <writeHost host="db3" url="10.0.0.52:6306" user="root" password="123456">

            <readHost host="db4" url="10.0.0.52:6307" user="root" password="123456" />

    </writeHost>

    </dataHost>

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

        <heartbeat>select user()</heartbeat> 

    <writeHost host="db11" url="10.0.0.51:5308" user="root" password="123456">

            <readHost host="db22" url="10.0.0.51:5309" user="root" password="123456" />

    </writeHost>

    <writeHost host="db33" url="10.0.0.52:6308" user="root" password="123456">

            <readHost host="db44" url="10.0.0.52:6309" user="root" password="123456" />

    </writeHost>

    </dataHost>

</mycat:schema>

3.6 范围分片:

此分片适用于提前规划好字段某个范围属于哪个分片

vim rule.xml

<tableRule name="auto-sharding-long">

                <rule>

                        <columns>umeta_id</columns>

                        <algorithm>rang-long</algorithm>

                </rule>

vim autopartition-long.txt

1-9=0

10-18=1

3.7 全局表:

在数据切分处理中,特别是水平切分中,中间件最终要的两个处理过程就是数据的切换和数据的聚合,选择合适的切分规则,至关重要,因为他决定了后续数据聚合的难易程度,甚至可以避免跨库的数据聚合处理

3.7.1 Mycat全局表:

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

<table name="t_area" primaryKey="id" type="global" dataNode="dn1,dn2" />

3.8 ER分片:

有一类业务,例如订单跟订单明细,明细表会依赖于订单表,也就是说会存在表的主从关系,这类业务的切分可以抽象出合适的切分规则,比如根据用户ID切分,其他相关的表都依赖于用户ID,在或者根据订单的ID切分,总之部分业务总会可以抽象出父子关系的表,这类的表的使用与ER分片表,子表的记录与所关联的父表记录存放在同一个数据分片上,避免数据join跨库操作

<table name="orders" dataNode="dn1,dn2" rule="mod-long">

<childTable name="order_detail" primaryKey="id" joinKey="order_id" parentKey="order_id" />

</table>

3.9 枚举分片:

通过在配置文件中配置可能的枚举id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或者区县来保存,而全国省份区县都是固定的,这种情况比较适用

<tableRule name="sharding-by-intfile">

<rule> <columns>user_id</columns>

<algorithm>hash-int</algorithm>

</rule>

</tableRule>

<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">

<property name="mapFile">partition-hash-int.txt</property>

<property name="type">0</property>

<property name="defaultNode">0</property>

</function>

修改partition-hash-int.txt 配置:

10000=0

10010=1

DEFAULT_NODE=1

columns 标识将要分片的表字段,algorithm 分片函数, 其中分片函数配置中,mapFile标识配置文件名称

3.10 分片的原则:

原则一:能不分就不分,1000万以内的表,不建议分片,通过合适的索引,读写分离等方式,可以很好的解决性能问题。

?原则二:分片数量尽量少,分片尽量均匀分布在多个DataHost上,因为一个查询SQL跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量。

?原则三:分片规则需要慎重选择,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性Hash分片,这几种分片都有利于扩容

?原则四:尽量不要在一个事务中的SQL跨越多个分片,分布式事务一直是个不好处理的问题

?原则五:查询条件尽量优化,尽量避免Select * 的方式,大量数据结果集下,会消耗大量带宽和CPU资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。

总体上来说,分片的选择是取决于最频繁的查询SQL的条件,因为不带任何Where语句的查询SQL,会便利所有的分片,性能相对最差,因此这种SQL越多,对系统的影响越大,所以我们要尽量避免这种SQL

第4章 报错

4.1 主从复制问题:

 

4.2 Mycat连接到数据库报无效数据库源(Invalid datasource)

mysql> show variables like '%server_id%';

ERROR 3009 (HY000): java.lang.IllegalArgumentException: Invalid DataSource:0

这类错误最常见是一些配置问题例如schema.xml中的dataNode的配置和实际不符合,请先确保配置没有问题。

如果不是配置问题,分析具体日志看出错原因,常见的有: 
如果是应用连:在某些版本的Mysql驱动下连接Mycat会报错,可升级最新的驱动包试下。 
如果是服务端控制台连,确认mysql是否开启远程连接权限,或防火墙是否设置正确,或者数据库database是否配置,或用户名密码是否正确。

解决办法:授权root用户即可

mysql> select user,host from mysql.user;

+------+-----------+

| user | host      |

+------+-----------+

| repl | 10.0.0.%  |

| root | 127.0.0.1 |

| root | ::1       |

|      | db01      |

| root | db01      |

|      | localhost |

| root | localhost

grant all on *.* to [email protected]'10.0.0.%' identified by '123456';


以上是关于MyCAT读写分离分库分表的主要内容,如果未能解决你的问题,请参考以下文章

mycat 读写分离+分库分表+全局表

Mycat 读写分离+分库分表

MyCAT读写分离分库分表

MyCat实现读写分离+分库分表+全局表

docker安装mycat并实现mysql读写分离和分库分表

mycat 分表分库-读写分离