注:本篇文章的操作均在centos7.5下。
mycat是mysql的中间件,它可以做mysql数据库的读写分离、分库分表。
安装:
1)安装java
因Mycat是用java开发的,所以需要在实验环境下安装java,官方建议jdk1.7及以上版本
Java Oracle官方下载地址为:
http://www.oracle.com/technetwork/java/javase/archive-139210.html
在环境变量中加入下载的jdk:
sudo vim /etc/profile
export JAVA_HOME=/usr/local/jdk1.8.0_191
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin
:wq
source /etc/profile
如果配置完成后出现以下报错:
这时需要安装一个:yum -y install glibc.i686。
2)安装mycat:
去mycat官网下载一个软件包,什么系统下载什么样的软件包!!!
Mycat官网:http://www.mycat.io/
可以了解下Mycat的背景和应用情况,这样使用起来比较有信心。
Mycat下载地址:http://dl.mycat.io/
官网有个文档,属于详细的介绍,初次入门,看起来比较花时间。
tar -xvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/
cd /usr/local/mycat
配置:
这时就已经安装完了,启动还需要修改4个文件:rule.xml、server.xml、schema.xml、wrapper.conf
修改rule.xml (这个文件中写的都是mycat分库分表的算法):
默认是不需要修改的。
修改server.xml(这个文件里面定义了用于配置系统参数和给逻辑表分配权限等):
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="defaultSqlParser">druidparser</property>
<property name="mutiNodeLimitType">1</property>
<property name="useSqlStat">1</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="mutiNodePatchSize">1000</property>
<property name="serverPort">8066</property>
<property name="managerPort">9066</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--
<firewall>
<whitehost>
<host host="127.0.0.1" user="mycat"/>
<host host="127.0.0.2" user="mycat"/>
</whitehost>
<blacklist check="false">
</blacklist>
</firewall>
-->
<user name="root">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>
<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
<user name="user">
<property name="password">user</property>
<property name="schemas">TESTDB</property>
<property name="readOnly">true</property>
</user>
</mycat:server>
里面配置了mycat的管理端口9066和服务端口8066,登陆的地址,逻辑表分配权限。
参数 | 说明 |
---|---|
user | 用户配置节点 |
--name | 登录的用户名,也就是连接Mycat的用户名 |
--password | 登录的密码,也就是连接Mycat的密码 |
--schemas | 数据库名,这里会和schema.xml中的配置关联,多个用逗号分开,例如需要这个用户需要管理两个数据库db1,db2,则配置db1,dbs |
--privileges | 配置用户针对表的增删改查的权限,具体见文档吧 |
里面有几个参数要注意:
idleTimeout:指定连接的空闲超时时间,默认是30分钟,所以在配置数据源的时候,数据源的超时时间要小于mycat的空闲超时时间。
charset:连接的初始化字符集,默认是utf8
txlsolation:前端连接的初始化事务隔离级别,默认是和mysql的repeated_read可重复读隔离级别一致
sqlExecuteTimeout:sql执行超时时间,默认是300秒。
重点!!!修改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">
<table name="user_freeze" primaryKey="id" subTables="user_freeze_2018$10-11,user_freeze" rule="sharding-by-month" dataNode="exchange" />
<table name="rate" primaryKey="id" dataNode="exchange" />
<table name="user_order" primaryKey="id" dataNode="exchange" />
<table name="user_identity" primaryKey="id" dataNode="exchange" />
<table name="user_account" primaryKey="id" dataNode="exchange" />
<table name="user_info" primaryKey="id" dataNode="exchange" />
<table name="user_withdraw" primaryKey="id" dataNode="exchange" />
<table name="user_recharge" primaryKey="id" dataNode="exchange" />
<table name="user_running" primaryKey="id" dataNode="exchange" />
<table name="user_exchange_log" primaryKey="id" dataNode="exchange" />
<table name="user_exchange" primaryKey="id" dataNode="exchange" />
<table name="user_transfer" primaryKey="id" dataNode="exchange" />
<table name="deals" primaryKey="id" subTables="deals_2018$10-12,deals_2019$1-2" rule="sharding-by-month" dataNode="exchange_bonus" />
<table name="user_et_running" primaryKey="id" dataNode="exchange_bonus" />
<table name="invite_relation" primaryKey="id" dataNode="exchange_bonus" />
<table name="invite_relation_usdt" primaryKey="id" dataNode="exchange_bonus" />
<table name="invite" primaryKey="id" dataNode="exchange_bonus" />
<table name="deals_signal" primaryKey="id" dataNode="exchange_bonus" />
<table name="bonus_signal" primaryKey="id" dataNode="exchange_bonus" />
<table name="airdrop_signal" primaryKey="id" dataNode="exchange_bonus" />
<table name="invite_count" primaryKey="id" dataNode="exchange_bonus" />
<table name="invite_usdt" primaryKey="id" dataNode="exchange_bonus" />
<table name="mine" primaryKey="id" dataNode="exchange_bonus" />
<table name="recharge_running" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_amount_day" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_amount_hour" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_fee_hour" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_user_amount_day" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_user_amount_hour" primaryKey="id" dataNode="exchange_bonus" />
<table name="stats_user_fee_hour" primaryKey="id" dataNode="exchange_bonus" />
<table name="user_airdrop" primaryKey="id" dataNode="exchange_bonus" />
<table name="user_bonus" primaryKey="id" dataNode="exchange_bonus" />
<table name="user_et_balance" primaryKey="id" dataNode="exchange_bonus" />
<!-- <table name="robot" primaryKey="id" dataNode="exchange_financial_risk" />
<table name="robot_recharge" primaryKey="id" dataNode="exchange_financial_risk" />-->
</schema>
<!-- <dataNode name="exchange_financial_risk" dataHost="dataHost01" database="exchange_financial_risk" />-->
<dataNode name="exchange" dataHost="dataHost02" database="exchange" />
<dataNode name="exchange_bonus" dataHost="dataHost01" database="exchange_bonus" />
<dataNode name="exchange_statistic" dataHost="dataHost01" database="exchange_statistic" />
<dataHost name="dataHost01" maxCon="100" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select 1</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://exchange-cluster.cluster-clwklichzgk5.ap-northeast-1.rds.amazonaws.com" user="app" password="exchain@2018">
<readHost host="hostS1" url="jdbc:mysql://exchange-cluster.cluster-ro-clwklichzgk5.ap-northeast-1.rds.amazonaws.com" user="app" password="exchain@2018" />
</writeHost>
</dataHost>
<dataHost name="dataHost02" maxCon="100" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select 1</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://exchain-cluster.cluster-clwklichzgk5.ap-northeast-1.rds.amazonaws.com" user="app" password="exchain@2018">
<readHost host="hostS1" url="jdbc:mysql://exchain-cluster.cluster-ro-clwklichzgk5.ap-northeast-1.rds.amazonaws.com" user="app" password="exchain@2018" />
</writeHost>
</dataHost>
</mycat:schema>
这里对连接的mysql做了读写分离和分库分表
参数 | 说明 |
---|---|
schema | 数据库设置,此数据库为逻辑数据库,name与server.xml中schema对应 |
dataNode | 分片信息,也就是分库相关配置 |
dataHost | 物理数据库,真正存储数据的数据库 |
schema:
schema标签用来定义mycat实例中的逻辑库,mycat可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用schema标签来划分这些不同的逻辑库。
如果不配置schema标签,所有表的配置会属于同一个默认的逻辑库。
逻辑库的概念和MySql的database的概念一样,我们在查询两个不同逻辑库中的表的时候,需要切换到该逻辑库下进行查询
注意:进行查询的时候需要在server.xml配置相关用户权限信息
属性 | 说明 |
---|---|
name | 逻辑数据库名,与server.xml中的schema对应 |
checkSQLschema | 数据库前缀相关设置,建议看文档,这里暂时设为folse |
sqlMaxLimit | select 时默认的limit,避免查询全表 |
table:
属性 | 说明 |
---|---|
name | 表名,物理数据库中表名 |
dataNode | 表存储到哪些节点,多个节点用逗号分隔。节点为下文dataNode设置的name |
primaryKey | 主键字段名,自动生成主键时需要设置 |
autoIncrement | 是否自增 |
rule | 分片规则名,具体规则下文rule详细介绍 |
subTables 支持按月分表 $201701-? 配置
基于MyCat1.6正式版的源码修改的,支持subTables的按月分表正则配置 subTables=“ tableName_$201701-?” subTableWay="BYMONTH" 表示从201701月份开始进行分表处理,?表示当前日期的月份,是动态的。只需配置开始日期即可,首先要确定的一点就是子表要在mysql里面真实存在,可以进行动态创建表达到子表的自动增长。subTableWay是表示按月,也可以按照日期,及subTableWay="BYDATE"
dataNode(标签定义了mycat中的数据节点,也就是我们所说的数据分片。一个datanode标签就是一个独立的数据分片。)
属性 | 说明 |
---|---|
name | 节点名,与table中dataNode对应 |
datahost | 物理数据库名,与datahost中name对应 |
database | 物理数据库中数据库名 |
dataHost
属性 | 说明 |
---|---|
name | 物理数据库名,与dataNode中dataHost对应 |
balance | 均衡负载的方式 |
writeType | 写入方式 |
dbType | 数据库类型 |
heartbeat | 心跳检测语句,注意语句结尾的分号要加。 |
maxCon
指定每个读写实例连接池的最大连接。
minCon
指定每个读写实例连接池的最小连接,初始化连接池的大小
balance
负载均称类型
0:不开启读写分离机制,所有读操作都发送到当前可用的writeHost上
1:全部的readHost与stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1-S1,M2-S2 并且M1 M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡。
2:所有读操作都随机的在writeHost、readHost上分发
3:所有读请求随机的分发到writeHst对应的readHost执行,writeHost不负担读写压力。(1.4之后版本有)
writeType属性
负载均衡类型。
0:所有的写操作发送到配置的第一个writeHost,第一个挂了切换到第二个。切换记录在文件dnindex.properties
1:所有的鞋操作都随机的发送到配置的writeHost,1.5以后版本废弃不推荐。
switchType属性
-1不自动切换
1 默认值 自动切换
2 基于MySql主从同步的状态决定是否切换
心跳语句为 show slave status
3 基于mysql galary cluster 的切换机制(适合集群)1.4.1
心跳语句为 show status like 'wsrep%'
dbType
指定后端链接的数据库类型目前支持二进制的mysql协议,还有其他使用jdbc链接的数据库
dbDriver属性
指定连接后段数据库使用的driver,目前可选的值有native和JDBC。使用native的话,因为这个值执行的是二进制的mysql协议,所以可以使用mysql和maridb,其他类型的则需要使用JDBC驱动来支持。
如果使用JDBC的话需要符合JDBC4标准的驱动jar 放到mycat\lib目录下,并检查驱动jar包中包括如下目录结构文件 META-INF\services\java.sql.Driver。 在这个文件写上具体的driver类名,例如com.mysql.jdbc.Driver
writeHost readHost
指定后端数据库的相关配置给mycat,用于实例化后端连接池
slaveThreshold="100"
Mycat心跳检查语句配置为 show slave status ,dataHost 上定义两个新属性: switchType=“2” 与slaveThreshold=“100”,此时意味着开启MySQL主从复制状态绑定的读写分离与切换机制。Mycat心跳机制通过检测 show slave status 中的 “Seconds_Behind_Master”, “Slave_IO_Running”,“Slave_SQL_Running” 三个字段来确定当前主从同步的状态以及Seconds_Behind_Master主从复制时延。
使用:
Mycat的启动也很简单,启动命令在Bin目录:
##启动
mycat start
##停止
mycat stop
##重启
mycat restart
如果在启动时发现异常,在logs目录中查看日志。
- wrapper.log 为程序启动的日志,启动时的问题看这个
- mycat.log 为脚本执行时的日志,SQL脚本执行报错后的具体错误内容,查看这个文件。mycat.log是最新的错误日志,历史日志会根据时间生成目录保存。
连接:
mysql -h127.0.0.1 -P8066 -uroot -p123456
Mycat还有一个管理的连接,端口号是9906.
mysql -h127.0.0.1 -P9066 -uroot -p123456
连接后可以根据管理命令查看Mycat的运行情况,当然,喜欢UI管理方式的人,可以安装一个Mycat-Web来进行管理,有兴趣自行搜索。
常见问题
-
Mycat是不是配置以后,就能完全解决分表分库和读写分离问题?
Mycat配合数据库本身的复制功能,可以解决读写分离的问题,但是针对分表分库的问题,不是完美的解决。或者说,至今为止,业界没有完美的解决方案。
分表分库写入能完美解决,但是,不能完美解决主要是联表查询的问题,Mycat支持两个表联表的查询,多余两个表的查询不支持。 其实,很多数据库中间件关于分表分库后查询的问题,都是需要自己实现的,而且节本都不支持联表查询
-
Mycat支持哪些数据库,其他平台如 .net、PHP能用吗?
官方说了,支持的数据库包括MySQL、SQL Server、Oracle、DB2、PostgreSQL 等主流数据库,很赞。
尽量用Mysql,我试过SQL Server,会有些小问题,因为部分语法有点差异。 -
Mycat 非JAVA平台如 .net、PHP能用吗?
可以用。这一点MyCat做的也很棒。
启动出现的错误:
1)启动mycat时报错信息如下:
[root@mycat ]# /usr/local/mycat/bin/mycat console start
Running Mycat-server...
wrapper | --> Wrapper Started as Console
wrapper | Launching a JVM...
jvm 1 | Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=64M; support was removed in 8.0
jvm 1 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.org
jvm 1 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.
jvm 1 |
jvm 1 |
jvm 1 | WrapperSimpleApp: Encountered an error running main: java.lang.ExceptionInInitializerError
jvm 1 | java.lang.ExceptionInInitializerError
jvm 1 | at org.opencloudb.MycatStartup.main(MycatStartup.java:50)
jvm 1 | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
jvm 1 | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
jvm 1 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
jvm 1 | at java.lang.reflect.Method.invoke(Method.java:498)
jvm 1 | at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
jvm 1 | at java.lang.Thread.run(Thread.java:748)
jvm 1 | Caused by: org.opencloudb.config.util.ConfigException: dataNode node_db01 reference dataHost:mycat not exists!
jvm 1 | at org.opencloudb.config.loader.xml.XMLSchemaLoader.createDataNode(XMLSchemaLoader.java:545)
jvm 1 | at org.opencloudb.config.loader.xml.XMLSchemaLoader.loadDataNodes(XMLSchemaLoader.java:516)
jvm 1 | at org.opencloudb.config.loader.xml.XMLSchemaLoader.load(XMLSchemaLoader.java:106)
jvm 1 | at org.opencloudb.config.loader.xml.XMLSchemaLoader.<init>(XMLSchemaLoader.java:71)
jvm 1 | at org.opencloudb.config.loader.xml.XMLSchemaLoader.<init>(XMLSchemaLoader.java:75)
jvm 1 | at org.opencloudb.ConfigInitializer.<init>(ConfigInitializer.java:63)
jvm 1 | at org.opencloudb.MycatConfig.<init>(MycatConfig.java:69)
jvm 1 | at org.opencloudb.MycatServer.<init>(MycatServer.java:105)
jvm 1 | at org.opencloudb.MycatServer.<clinit>(MycatServer.java:73)
jvm 1 | ... 7 more
wrapper | <-- Wrapper Stopped
2、解决方法:
修改schema.xml
<dataHost name="mycat" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="mycat" url="192.168.4.116:3306" user="root"
password="123456">
<!-- can have multi read hosts -->
<!--<readHost host="hostS2" url="192.168.1.200:3306" user="root" password="xxx" />-->
</writeHost>
<!--<writeHost host="hostS1" url="localhost:3316" user="root"-->
<!--password="123456" />-->
<!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> -->
</dataHost>
2)url直接写成域名报错!
报错信息如下:
WrapperSimpleApp: Encountered an error running main: java.lang.ExceptionInInitializerError
INFO | jvm 1 | 2018/11/16 11:26:15 | java.lang.ExceptionInInitializerError
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.MycatStartup.main(MycatStartup.java:53)
INFO | jvm 1 | 2018/11/16 11:26:15 | at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
INFO | jvm 1 | 2018/11/16 11:26:15 | at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
INFO | jvm 1 | 2018/11/16 11:26:15 | at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
INFO | jvm 1 | 2018/11/16 11:26:15 | at java.lang.reflect.Method.invoke(Method.java:606)
INFO | jvm 1 | 2018/11/16 11:26:15 | at org.tanukisoftware.wrapper.WrapperSimpleApp.run(WrapperSimpleApp.java:240)
INFO | jvm 1 | 2018/11/16 11:26:15 | at java.lang.Thread.run(Thread.java:745)
INFO | jvm 1 | 2018/11/16 11:26:15 | Caused by: io.mycat.config.util.ConfigException: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.load(XMLSchemaLoader.java:126)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.<init>(XMLSchemaLoader.java:83)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.<init>(XMLSchemaLoader.java:87)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.ConfigInitializer.<init>(ConfigInitializer.java:74)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.MycatConfig.<init>(MycatConfig.java:72)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.MycatServer.<init>(MycatServer.java:144)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.MycatServer.<clinit>(MycatServer.java:96)
INFO | jvm 1 | 2018/11/16 11:26:15 | ... 7 more
INFO | jvm 1 | 2018/11/16 11:26:15 | Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
INFO | jvm 1 | 2018/11/16 11:26:15 | at java.lang.String.substring(String.java:1911)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.createDBHostConf(XMLSchemaLoader.java:687)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.loadDataHosts(XMLSchemaLoader.java:776)
INFO | jvm 1 | 2018/11/16 11:26:15 | at io.mycat.config.loader.xml.XMLSchemaLoader.load(XMLSchemaLoader.java:118)
INFO | jvm 1 | 2018/11/16 11:26:15 | ... 13 more
STATUS | wrapper | 2018/11/16 11:26:17 | <-- Wrapper Stopped
STATUS | wrapper | 2018/11/16 11:32:57 | --> Wrapper Started as Daemon
解决方法:需要修改dbDriver为jdbc,url需要加前缀jdbc:mysql://
dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select 1</heartbeat>
<writeHost host="hostM1" url="jdbc:mysql://exchange-cluster.cluster-clwklichzgk5.ap-northeast-1.rds.amazonaws.com" user="app" password="exchain@2018">
3)还有1个报错会在启动时直接在屏幕上输出找不到二进制文件的错误
输出这个错误是由于wrapper.conf文件java的配置有问题,
错误的:wrapper.java.command=/data/mycat/jdk1.7.0_80/(只写java的home路径会报错)
正确的:wrapper.java.command=/data/mycat/jdk1.7.0_80/bin/java(需要写上java命令的路径)