1 背景
最近工作需要对比几种数据库技术方案,主从读写分离集群也是其中之一。接着上一篇文章《MySQL主从数据库搭建》基础上继续搭建MyCat主从集群。
2 MyCat
什么是MyCat?MyCat是数据库中间件,就是介于数据库和应用之间,进行数据处理与交互的中间服务。可以对数据进行分片处理,从原有一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。
如上图所示,数据被分到多个分片数据库后,应用如果需要读取数据,就需要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片的问题,最重要的是每个应用 处理将是完全的重复造轮子。
所以有了数据库中间件,应用只需要集中于业务处理,大量的通用的数据聚合,事务,数据源切换都有中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,所以一款好的数据库中间件至关重要。
MyCat可以做主从也可以做分库分表,本文主要记录主从集群搭建方式。
2.1 部署架构
目前我的虚拟机环境已有docker环境,MySQL主从集群环境,已经制作了MyCat镜像,并且虚拟出来1个MyCat服务实例。
2.2 配置文件
配置主从数据库集群最重要的就是修改schema.xml文件
2.3.1 schema.xml
这是我这边的测试配置,可以根据具体情况适当修改。
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1" dataNode="dn1">
<table name="test_table_1" primaryKey="id" autoIncrement="false" dataNode="dn1" />
<table name="test_table_2" primaryKey="id" autoIncrement="false" dataNode="dn1" />
<table name="test_table_3" primaryKey="id" autoIncrement="false" dataNode="dn1" />
</schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="master1" url="jdbc:mysql://172.16.17.231:3306?characterEncoding=utf8" user="root" password="123456">
<readHost host="slave1" url="jdbc:mysql://172.16.17.232:3306?characterEncoding=utf8" user="root" password="123456" />
</writeHost>
</dataHost>
</mycat:schema>
这里我解释一下上述配置文件中出现标签的含义。
1 schema 标签 schema标签用于定义MyCat实例中的逻辑库,MyCat可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用schema标签来划分这些逻辑库。如果不配置schema标签,所有的表配置,会属于同一个默认的逻辑库。
属性名 | 值 | 数量限制 | 含义 |
---|---|---|---|
dataNode | 任意String | (0…1) | 用于绑定逻辑库到具体的database上 |
checkSQLschema | Boolean | (1) | 用于移除SQL语句中表示schema的字符去掉 |
sqlMaxLimit | Integer | (1) | 用于限制SQL查询记录的条数 |
2 table标签 table标签定义了MyCat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。
属性名 | 值 | 数量限制 | 含义 |
---|---|---|---|
name | String | (1) | 用于定义逻辑表的表名 |
dataNode | String | (1…*) | 用于定义这个逻辑表所属的dataNode,该属性的值需要和dataNode标签中name属性的值相互对应 |
rule | String | (0…1) | 用于指定逻辑表需要使用的规则名字,规则名字在rule.xml中定义,必须与tableRule标签中name属性的属性值一一对应。 |
ruleRequired | boolean | (0…1) | 该属性用于指定表是否绑定分片规则,如果配置为true,但没有配置具体rule的话,程序会报错。 |
primaryKey | String | (1) | 用于逻辑表对应真实表的主键 |
type | String | (0…1) | 该属性定义了逻辑表的类型,目前逻辑表只有“全局表”和“普通表”两种类型 |
autoIncrement | boolean | (0…1) | 用于指定这个表使用自增长主键,这样MyCat才会不抛出分片键找不到的异常 |
subTables | String | (1) | |
needAddLimit | boolean | (0…1) | 指定表是否需要自动在每个语句后面加上limit限制 |
3 dataNode标签 dataNode标签定义了MyCat中的数据节点,也就是通常说的数据分片。
属性名 | 值 | 数量限制 | 含义 |
---|---|---|---|
name | String | (1) | 用于定义数据节点的名字,这个名字需要是唯一的,用来建立表和分片的对应关系 |
dataHost | String | (1) | 用于定义该分片属于哪个数据库实例 |
database | String | (1) | 用于定义该分片属性哪个具体数据库实例上的具体库 |
4 dataHost标签 该标签在MyCat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。
属性名 | 值 | 数量限制 | 含义 |
---|---|---|---|
name | String | (1) | 唯一标识dataHost标签,供上层的标签使用 |
maxCon | Integer | (1) | 指定每个读写实例连接池的最大连接 |
minCon | Integer | (1) | 指定每个读写实例连接池的最小连接,初始化连接池的大小 |
balance | Integer | (1) | 负载均衡类型,0:不开启读写分离机制,所有读操作都发送到当前可用的writeHost上;1:全部的readHost与stand by writeHost参与select语句负载均衡,简单的说,当双主双从模式(M1->S1,M2-S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡;2:所有读操作都随机的在writeHost、readHost上分发;3:所有读请求随机的分发到writeHost对应的readHost执行,writeHost不负担读压力,注意balance=3只在1.4及其以后版本有,1.3没有 |
writeType | Integer | (1) | 负载均衡类型,0:所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个writeHost,重新启动后已切换后为准,切换记录在配置文件中:dnindex.properties;1:所有写操作都随机的发送到配置的writeHost,1.5以后废弃不推荐 |
dbType | String | (1) | 指定后端连接的数据库类型,目前支持二进制的mysql协议,还有其他使用JDBC连接的数据库。例如:MongoDB、Oracle、Spark等 |
dbDriver | String | (1) | 指定连接后端数据库使用的Driver,目前可选的值有native和jdbc |
switchType | Integer | (1) | -1:表示不自动切换;1:默认值,自动切换;2:基于MySQL主从同步状态决定是否切换;基于MySQL galary cluster的切换机制(适合集群)(1.4.1) |
2.3.2 server.xml
server.xml中的标签本就不多,这里就不再一一列举了。
<?xml version="1.0" encoding="UTF-8"?>
<!-- - - Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License. - You
may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0
- - Unless required by applicable law or agreed to in writing, software -
distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the
License for the specific language governing permissions and - limitations
under the License. -->
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="charset">utf8mb4</property>
<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->
<property name="ignoreUnknownCommand">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。
在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->
<property name="useHandshakeV10">1</property>
<property name="removeGraveAccent">1</property>
<property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 -->
<property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时 单位:秒-->
<property name="sequenceHandlerType">1</property>
<!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx");
-->
<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
<property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
<!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->
<!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->
<!-- <property name="processorBufferChunk">40960</property> -->
<!--
<property name="processors">1</property>
<property name="processorExecutor">32</property>
-->
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
<property name="processorBufferPoolType">0</property>
<!--默认是65535 64K 用于sql解析时最大文本长度 -->
<!--<property name="maxStringLiteralLength">65535</property>-->
<!--<property name="sequenceHandlerType">0</property>-->
<!--<property name="backSocketNoDelay">1</property>-->
<!--<property name="frontSocketNoDelay">1</property>-->
<!--<property name="processorExecutor">16</property>-->
<!--
<property name="serverPort">8066</property> <property name="managerPort">9066</property>
<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
<property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查
<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!--
off heap for merge/order/group/limit 1开启 0关闭
-->
<property name="useOffHeapForMerge">0</property>
<!--
单位为m
-->
<property name="memoryPageSize">64k</property>
<!--
单位为k
-->
<property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!--
单位为m
-->
<property name="systemReserveMemorySize">384m</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">false</property>
<!-- XA Recovery Log日志路径 -->
<!--<property name="XARecoveryLogBaseDir">./</property>-->
<!-- XA Recovery Log日志名称 -->
<!--<property name="XARecoveryLogBaseName">tmlog</property>-->
<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
<property name="strictTxIsolation">false</property>
<!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行-->
<property name="parallExecute">0</property>
</system>
<!-- 全局SQL防火墙设置 -->
<!--白名单可以使用通配符%或着*-->
<!--例如<host host="127.0.0.*" user="root"/>-->
<!--例如<host host="127.0.*" user="root"/>-->
<!--例如<host host="127.*" user="root"/>-->
<!--例如<host host="1*7.*" user="root"/>-->
<!--这些配置情况下对于127.0.0.1都能以root账户登录-->
<!--
<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>
<property name="defaultSchema">TESTDB</property>
<!--No MyCAT Database selected 错误前会尝试使用该schema作为schema,不设置则为null,报错 -->
<!-- 表级 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>
<property name="defaultSchema">TESTDB</property>
</user>
</mycat:server>
3 测试
完成上述配置,重启MyCat,通过应用程序或者数据库客户端来验证一下是否生效。
docker restart mycat