Mycat 是一个开源的数据库中间件,能够解决分布式数据库环境下的大多数问题,如读写分离、分库分表等,除此以外,它还具有如下特性:php
在引入 Mycat 后,全部的客户端请求须要通过中间件进行转发上,此时客户端直接面向的是 Mycat 上的逻辑库或逻辑表:java
在 Mycat 的配置文件中进行定义,它对应一个或者多个实际的数据库或数据库集群。node
能够对应一张实际的表,也能够表示为多个分片表的集合。按其特性能够分为如下四类:mysql
将表按照分片键进行分片后,一个表中的全部数据就会被分发到不一样的数据库上,这些数据库节点就称为分片节点。git
在 Mycat 的安装目录的 conf
目录下,有如下三个核心配置文件:github
一个 server.xml 的配置示例以下,主要包含如下标签:算法
<system>
<!-- 0为须要密码登录、1为不须要密码登录 ,默认为0,设置为1则须要指定默认帐户-->
<property name="nonePasswordLogin">0</property>
<property name="charset">utf8</property>
</system>
<firewall>
<whitehost>
<host host="1*7.0.0.*" user="root"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
<!-- 定义默认帐户-->
<user name="root" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<!-- 权限设置 -->
<privileges check="true">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
</user>
<!-- 定义默认帐户-->
<user name="user">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
复制代码
在上面的示例中,用户配置的是明文密码,这样会存在安全隐患,所以 Mycat 也支持对密码进行加密,示例以下:sql
# 该jar包在Mycat安装目录的lib目录下
shell > java -cp Mycat-server-1.6.7.1-release.jar io.mycat.util.DecryptUtil 0:root:123456
GO0bnFVWrAuFgr1JMuMZkvfDNyTpoiGU7n/Wlsa151CirHQnANVk3NzE3FErx8v6pAcO0ctX3xFecmSr+976QA==
复制代码
能够将明文密码替换为加密密码,但此时还需在对应用户的 user 标签下增长以下配置,表明启用加密功能:shell
<property name="usingDecrypt">1</property>
复制代码
下面是一个示例的 schema.xml 配置文件,主要包含如下标签:数据库
select * from db.table1
等查询时,会自动修改成 select * from table1
。sqlMaxLimit 用于限制返回数据的行数。<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
<!-- mod-long 分片规则在下文的 rule.xml中定义 -->
<table name="order" dataNode="dn1,dn2,dn3" rule="mod-long" />
</schema>
<dataNode name="dn1" dataHost="cluster01" database="order_db" />
<dataNode name="dn2" dataHost="cluster02" database="order_db" />
<dataNode name="dn3" dataHost="cluster03" database="order_db" />
<dataHost name="cluster01" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<!-- 心跳检查 -->
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.226:3306" user="root" password="123456">
<readHost host="salve" url="192.168.0.227:3306" user="root" password="123456" />
</writeHost>
</dataHost>
...... 省略 cluster02,cluster03
复制代码
这里解释一下 dataHost 标签的相关属性:maxCon 表示最大链接数,minCon 表示最小链接数,dbType 表示数据库类型 ( 如 mysql、oracle 等),其余属性都有多个可选值,具体以下:
dbDriver:数据库类型,可选的值有 native 和 JDBC,若是是 mysql,maridb,postgresql 等数据库,直接可使用 native 便可。其余数据库则须要将对应的驱动包拷贝到 Mycat 安装目录的 lib 目录下,并写上完整的驱动类的类名。
switchType:
balance:
balance="0"
:不开启读写分离机制,全部读请求都发送到当前可用的 writeHost 上。balance="1"
:所有的 readHost 与 stand by writeHost 参与读操做。stand by writeHost 一般指的是双主复制中处于 stand 状态的主节点,即假设集群复制架构为 Master1 -> Slave1,Master2 -> Slave2,而且 M1 与 M2 互为主备 ),此时 Master2 ,Slave1,Slave2 都会参与读的负载。balance="2"
:全部读请求随机在 writeHost、 readhost 上进行分发。balance="3"
:全部读请求随机分发到 writeHost 对应的 readhost 执行,writerHost 不负担读压力。rule.xml 文件中定义的是分片规则,主要包含如下标签:
<tableRule name="mod-long">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- nodes 节点的数量-->
<property name="count">3</property>
</function>
复制代码
Mycat 内置支持十几种分片算法,如 取模分片,枚举分片,范围分片,字符串 hash 分片,一致性 hash 分片,日期分片等。关于这些分片算法的详细说明能够参考官方文档:Mycat 官方指南
Mycat 读写分离的配置很是简单,只须要经过配置 balance,writeHost 和 readHost 就能够实现,示例以下:
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native">
<heartbeat>select user()</heartbeat>
<!-- 能够配置多个writeHost -->
<writeHost host="Master" url="hostname1:3306" user="root" password="123456">
<!-- 能够配置多个readHost-->
<readHost host="Slave" url="hostname2:3306" user="root" password="123456" />
</writeHost>
</dataHost>
复制代码
可是须要注意的是如上的配置仍是会存在单点问题,由于只有一个 writeHost ,Mycat 支持配置多个 writeHost,示例以下:
<writeHost host="Master" url="hostname1:3306" user="root" password="123456">
<!-- 能够配置多个readHost-->
<readHost host="Slave1" url="hostname2:3306" user="root" password="123456" />
<readHost host="Slave2" url="hostname3:3306" user="root" password="123456" />
</writeHost>
<writeHost host="Slave3" url="hostname4:3306" user="root" password="123456" />
复制代码
以上是 Mycat 官方指南中给出的配置,即在一主三从的复制架构下,能够选择其中一个 Slave 为备用的写入节点,此时当 Master 节点宕机后,会继续在该备用节点执行写入操做。这个配置和架构存在如下两个问题:
基于以上两个缘由,若是想要实现高可用,并不建议配置多个 writeHost ,而是配置一个 writeHost ,但其指向的是虚拟的读 IP 地址,此时复制架构由 MMM 或者 MHA 架构来实现,并由它们来提供虚拟机的读 IP。
综合以上所有内容,这里给出一个分库分表的示例,其架构以下:
如上图所示,这里模拟的是一个电商数据库,并对其执行分库分表操做:
为节省篇幅,以上全部测试表和测试库的创建语句单独整理至:ec_shop.sql 。分库分表的具体操做以下:
这里新增一个 Mycat 用户,并定义其管理的逻辑数据库为 ec_shop,另外使用 fakeMySQLVersion 来定义你所须要模拟的 MySQL 数据库的版本。若是没有特殊需求, Mycat 自带的 server.xml 中的其余配置可不作更改:
<system>
<property name="fakeMySQLVersion">5.7.20</property>
</system>
<user name="heibaiying">
<property name="password">
B+BlA/U17pjyzHslglpDgYUxpgqK8qC62IRt/S74RBW6R7dZFJAXVb5tJDgmhzM4fj14MMhLnNmvKko6D73+iA==
</property>
<property name="schemas">ec_shop</property>
<property name="usingDecrypt">1</property>
</user>
复制代码
这里使用 childTable 来将订单表和订单明细表定义为 ER 表,避免跨分片查询。并将地址表 area_info 使用 type="global"
声明为全局,一样也是为了不跨分片查询:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="ec_shop" checkSQLschema="false" sqlMaxLimit="100">
<table name="customers" primaryKey="customer_id" dataNode="dn01"/>
<table name="products" primaryKey="product_id" dataNode="dn02"/>
<table name="orders" primaryKey="order_id" dataNode="dn03,dn04" rule="mod-long">
<childTable name="order_detail" primaryKey="order_detail_id" joinKey="order_id" parentKey="order_id"/>
</table>
<table name="area_info" primaryKey="area_id" type="global" dataNode="dn01,dn02,dn03,dn04"/>
</schema>
<dataNode name="dn01" dataHost="host01" database="ec_shop_customer"/>
<dataNode name="dn02" dataHost="host02" database="ec_shop_product"/>
<dataNode name="dn03" dataHost="host03" database="ec_shop_order"/>
<dataNode name="dn04" dataHost="host04" database="ec_shop_order"/>
<dataHost name="host01" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.226:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host02" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.227:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host03" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.228:3306" user="root" password="123456"/>
</dataHost>
<dataHost name="host04" maxCon="1000" minCon="10" balance="0" dbType="mysql" dbDriver="native" switchType="1">
<heartbeat>select user()</heartbeat>
<writeHost host="master" url="192.168.0.229:3306" user="root" password="123456"/>
</dataHost>
</mycat:schema>
复制代码
定义订单表所使用的分片规则,这里使用取模算法做为示例:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:rule SYSTEM "rule.dtd">
<mycat:rule xmlns:mycat="http://io.mycat/">
<tableRule name="mod-long">
<rule>
<columns>order_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- nodes 节点的数量-->
<property name="count">2</property>
</function>
</mycat:rule>
复制代码
这里我后端使用的数据库是 MySQL 8.0.17 ,相比于使用 MySQL 5.6 或 5.7 来整合 Mycat ,多了一些注意事项,主要以下:
即使你在 server.xml 中正确的配置了用户名和密码,但在使用 mysql shell 链接 Mycat 时,仍是会抛出密码错误的异常:
Access denied for user 'xxx', because password is error
复制代码
这是因为从 MySQL 8.0.4 开始使用 caching_sha2_password
做为认证的插件,而以前版本的插件为 mysql_native_password
,我在测试中使用的 Mycat 版本为 1.6.7,它并不支持 caching_sha2_password
。所以在登陆时候须要经过 --default_auth
来指定使用原有的认证插件:
# 1.6.7 版本 Mycat 默认的链接端口号为 8066
mysql -uheibaiying -p -h127.0.0.1 -P8066 --default_auth=mysql_native_password
复制代码
Mycat 和 MySQL 都正常启动,可是在 Mycat 上执行 SQL 语句失败,提示无效的数据库。此时能够查看 Mycat logs 目录下的 mycat.log 文件,一般会出现下面所示的异常:
(io.mycat.backend.mysql.nio.MySQLConnectionAuthenticator.handle(MySQLConnectionAuthenticator.java:91)
- can't connect to mysql server ,errmsg:Client does not support authentication protocol requested by
server; consider upgrading MySQL client MySQLConnection
复制代码
这和上面是一样的缘由,都是由于认证插件而致使的问题。此时须要修改帐户所使用的认证插件:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'xxxx';
FLUSH PRIVILEGES;
复制代码
修改后可使用以下命令进行查看:
mysql> SELECT Host,User,plugin FROM mysql.user;
+---------------+------------------+-----------------------+
| Host | User | plugin |
+---------------+------------------+-----------------------+
| % | root | mysql_native_password |
| 192.168.200.% | repl | mysql_native_password |
| localhost | mysql.infoschema | caching_sha2_password |
| localhost | mysql.session | caching_sha2_password |
| localhost | mysql.sys | caching_sha2_password |
| localhost | root | mysql_native_password |
+---------------+------------------+-----------------------+
复制代码
以后再重启 Mycat 服务就能够正常链接。
Mycat 官方指南:www.mycat.io/document/my…
更多文章,欢迎访问 [全栈工程师手册] ,GitHub 地址:github.com/heibaiying/…