文章目录
- 什么是Mycat
- 为什么要用Mycat
- mycat能干什么
- 各数据库中间件对比
- Mycat原理
- 数据库中间件
- 逻辑库
- 逻辑表
- 分片表
- 分片规则
- 全局表
- ER表
- 非分片表
- 分片节点
- 节点主机
- mycat安装
- mycat核心配置
- schema.xml
- server.xml
- rule.xml
- 加密明文密码(可选)
- MyCat读写分离
- 垂直拆分--分库
- 水平拆分--分表
- 来源
什么是Mycat
mycat是Java语言开发的目前比较主流的数据库中间件之一,是mycat团队在阿里开源的 Cobar 产品基础上而研发的,也一直在维护。mycat实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用 MySQL 原生协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信。
mycat不单只可以做MySQL的代理,它的后端可以支持MySQL、SQL Server、Oracle、DB2、PostgreSQL 等主流数据库,也支持 MongoDB 这种新型NoSQL 方式的存储,未来还会支持更多类型的存储。
为什么要用Mycat
- Java与数据库的紧耦合
- 高访问量高并发对数据库的压力
- 读写请求数据不一致
我们现在普遍的Java应用程序都是直接连接了MySQL软件进行读写操作,也就是我们在Java中的配置文件等定义了mysql的数据源,直接连接到了我们的mysql软件,但是当某些情况下我们可能需要用到了多个数据库,这个时候我们可能就需要配多个数据源去连接我们的多个数据库,这个时候我们进行sql操作的时候就会很麻烦,因为Java与数据库有了一个紧密的耦合度,但是如果我们在Java应用程序与mysql中间使用了mycat,我们只需要访问mycat就可以了,至于数据源等问题,mycat会直接帮我们搞定。
再来说一下高访问量高并发,我们都知道mysql数据库实际上在数据查询上是有一个瓶颈的,当我们的数据太多的时候,已经互联网上有高并发的请求的时候,这个时候对我们mysql的压力是非常大的,当访问量一大,就可能会出现查不出数据,响应的时间太长等,这个时候我们可能需要有多个服务器对数据库进行读写分离,以及对数据库进行集群,这个时候我们的sql语句要进行分类,哪个sql语句要访问哪个数据库,这个时候只要交给mycat就可以了。
最后说一下,使用多个数据库的时候我们就会遇到一个读写数据不一致的问题,这个时候同样mycat可以进行主从复制,保证了数据的一致性。
mycat能干什么
1、读写分离
2、数据分片
垂直拆分(分库)、水平拆分(分表)、垂直+水平(分库分表)
首先我们的数据库有多个表
当我们的表足够多的时候,也会造成整个数据库的瓶颈,这个时候查询是非常慢的,这个时候我们可能要对这个数据库进行垂直拆分,也就是分库
我们需要垂直拆分了表4 5 6 放到另外一个库中。
当我们垂直拆分了之后,可能又会出现单个表中的数据达到千万以上,这个时候对表造成了一个瓶颈,这个时候我们对表进行拆分。
我们可以把表的一部分数据拆分到另外的一个数据库。
3、多数据源整合
比如像一些非常重要的数据 比如订单 账单等数据,我们可以让mycat同时写入到多个数据库,保证数据的高可靠性。
也可以让mycat对一些数据进行多个主从的复制,有多个主从数据进行同步,当有一个主机坠机的时候,另外的主机进行切换保障数据安全。
另外我们可以用mycat连接到Nosql,当我们有一些非业务性的数据比如日志等的数据储存到Nosql中
各数据库中间件对比
- Cobar属于阿里B2B事业群,始于2008年,在阿里服役3年多,接管3000+个MySQL数据库的schema,集群日处理在线SQL请求50亿次以上。由于Cobar发起人的离职,Cobar停止维护。
- Mycat是开源社区在阿里cobar基础上进行二次开发,解决了cobar存在的问题,并且加入了许多新的功能在其中。青出于蓝而胜于蓝。
- OneProxy基于MySQL官方的proxy思想利用c进行开发的,OneProxy是一款商业收费的中间件。舍弃了一些功能,专注在性能和稳定性上。
- kingshard由小团队用go语言开发,还需要发展,需要不断完善。
- Vitess是Youtube生产在使用,架构很复杂。不支持MySQL原生协议,使用需要大量改造成本。
- Atlas是360团队基于mysql proxy改写,功能还需完善,高并发下不稳定。
- MaxScale是mariadb(MySQL原作者维护的一个版本) 研发的中间件
- MySQLRoute是MySQL官方Oracle公司发布的中间件
Mycat原理
Mycat 的原理中最重要的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,首先对 SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
这种方式把数据库的分布式从代码中解耦出来,程序员察觉不出来后台使用 Mycat 还是MySQL。
数据库中间件
Mycat是一个介于数据库与应用之间,进行数据处理与交互的中间服务。如下图:
通俗点讲,应用层可以将它看作是一个数据库的代理(或者直接看成加强版数据库)。 但是由于真正的数据库需要存储引擎,而 Mycat 并没有存储引擎,Mycat服务不保存真正的数据,所以并不是完全意义的分布式数据库系统。所以称Mycat这样类型的数据库中间代理服务的产品为数据库中间件。
逻辑库
对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。
如图一中,在MYCAT服务区中的db_user库,只是逻辑上存在的数据库,真正的数据来源还是来源MYSQL服务区中的两台实际的Mysql db实例。
在Mycat中逻辑库在{MYCAT_HOME}/conf/schema.xml 用 标签定义。如图:
逻辑表
既然有逻辑库,肯定将会存在逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。
逻辑表的数据来源,可以是数据进行切分后,分布在一个或多个分片库中,针对不同的数据分布和管理特点,我们将逻辑表又分为分片表、全局表、全局表、ER表、非分片表五种逻辑表类型。在schema.xml使用
标签对逻辑表进行定义。分片表
是指那些原有的很大数据的表,需要切分到多个表,这样,每个分片都有表的一部分数据,所有分片数据的合集构成了完整的表数据,如图一种中MYCAT服务区的users表即是分片表,通过userID字段取模的方式进行数据的水平切分。如图中用户(users)表:
分片规则
将大数据的表,切分到多个数据分片的策略。如图三中rule=“mod-userID-long”,名字为mod-userID-long引用的详细规则,将在MYCAT的rule.xml中({MYCAT_HOME}/conf/rule.xml)中进行定义,具体定义规则如图:
分片规则Mycat中内置了很多种,比如按时间、按自定义数字范围、十进制取模、程序指定,字符串Hash,一致性Hash等等,总体可将这些分片规则分为离散型和连续型两种分片规则。
离散型分片规则数据分布均衡,对数据的处理并发能力强,但是对于分片的扩缩容存在较大的挑战。连续性分片数据分布较集中,更符合业务特性,但是对数据的处理并发能力受限数据的分布,分片的扩缩容有更好的支持。
全局表
一个真实的业务系统中,往往存在大量的类似数据字典表的表,数据字典表具有以下几个特性:
• 数据变动不频繁;
• 数据规模不大,数据量在十万以内;
• 存在跟其他表(特别是分片表)有一点的关联查询要求。
未了解决表与表的join查询,Mycat提倡大家将具有上诉特点的表通过数据冗余的方式(全局表的定义)进行解决,即所有的分片都有一份数据的拷贝。通过MYCAT对这样的表进行数据的操作时,数据的修改,新增,删除时,所有的分片数据都将受到影响。
ER表
关系型数据库是基于实体关系模型(Entity-Relationship Model)之上,通过其描述了真实世界中事物与关系,Mycat 中的 ER 表即是来源于此。
根据这一思路,提出了基于 E-R 关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据 Join 不会跨库操作。
如文中的案例,用户表是分片表,用户地址表与用户表之间存在一对多的关系,若通过分片规则,将用户表中的张三分在了分片1,则最好的数据存储方式是将张三的用户地址信息跟随张三一起分配在分片1中。
这样一种表分组的设计方式是解决跨分片数据 join 的一种很好的思路,也是数据切分规划的重要一条规则。ER表中在schema.xml中使用<childTable>
标签进行描述和定义,如图:
非分片表
一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。在schema.xml中具体的定义,可参见图:
分片节点
大数据表进行数据切分后,每个表分片所在的数据库就是分片节点,狭义的理解可以认为一个DB实例就是一个节点,在schema.xml中使用<dataNode>
进行分片节点的定义如图:
节点主机
数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机,为了规避单节点主机并发数限制。
尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机,在schema.xml中使用<dataHost>
进行分片节点的定义如图:
mycat安装
官网地址:http://www.mycat.org.cn/
安装包下载地址:http://dl.mycat.org.cn/
mycat有windows版和Linux版,这里只说Linux的安装
PS:使用mycat前必须安装Java的JDK环境,so easy,这里不讲了,一定记得提前安装!!!
wget http://dl.mycat.org.cn/1.6.7.6/20210730131311/Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
tar -C /usr/local -zxvf Mycat-server-1.6.7.6-release-20210730131311-linux.tar.gz
cd /usr/local/mycat/
#./bin/mycat { console | start | stop | restart | status | dump }
./bin/mycat start
连接测试(mycat默认端口是8066,默认密码是123456)
root@f15fc642fedf:/# mysql -uroot -p123456 -P8066 -h192.168.241.140
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 3
Server version: 5.6.29-mycat-1.6.7.6-release-20210730131311 MyCat Server (OpenCloudDB)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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>
mycat核心配置
mycat的配置文件在 conf 文件夹下,最核心的配置文件有三个:schema.xml、server.xml、rule.xml
schema.xml
该配置文件主要是用于配置逻辑库、逻辑表等相关信息
- schema标签
定义mycat实例中的逻辑库,mycat可以有多个逻辑库,每个逻辑库都有自己的相关配置。可以使用schema标签来划分这些不同的逻辑库。如果不配置schema标签,所有表的配置会属于同一个默认的逻辑库。
逻辑库的概念和MySQL的database的概念一样,在查询两个不同逻辑库中的表的时候,需要切换到该逻辑库下进行查询。示例:
<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"></schema>
name
:定义逻辑库名,必须唯一不能重复
checkSQLschema
:检查发给mycat的SQL是否含有库名。当该值为true时,例如执行语句 select * from TESTDB.company mycat
会把语句修改为 select * from company
去掉库名 TESTDB
sqlMaxLimit
:限制返回结果集的行数。该值设置为某个数值时,每条执行的sql语句,如果没有加上limit参数,mycat会自动加上这个属性的值。不写的话,默认返回所有的值。需要注意的是,如果运行的schema为非拆分库的,那么该属性不会生效。需要自己sql语句加limit。
randomDataNode
:这是新版mycat新加入的属性,定义将一些随机语句发送到该数据节点中
- table 标签
定义mycat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。示例:
<table name="customer" primaryKey="id" dataNode="dn1,dn2" rule="sharding-by-intfile" autoIncrement="true" fetchStoreNodeByJdbc="true">
<childTable name="customer_addr" primaryKey="id" joinKey="customer_id" parentKey="id"> </childTable>
</table>
name
:定义逻辑表名,同一个schema中逻辑表名必须唯一
primaryKey
:指定逻辑表中的主键,也是需要与物理表的主键一致
dataNode
:指定物理表所在数据节点的名称,该属性的值需要和dataNode标签中的name值相对应。配置多个以逗号隔开,或者使用 dn1$0-100 方式
rule
:指定逻辑表使用的分片规则名称,规则名称在rule.xml中定义,必须与tableRule标签中name属性对应
autoIncrement
:是否自增,mycat提供了自增长主键功能,但是对应的mysql节点上数据表,没有
auto_increment,那么在mycat层调用last_insert_id()也是不会返回结果的fetchStoreNodeByJdbc:是否启用ER表使用JDBC方式获取DataNode
2.1)childTable标签:用于定义 ER 分片的子表。通过标签上的属性与父表进行关联
name
:子表名称
primaryKey
:主键
joinKey
:子表中字段的名称
parentKey
:父表中字段名称
- dataNode标签
定义mycat中的数据节点,也就是我们所说的数据分片。一个dataNode标签就是一个独立的数据分片。数据节点指向的是存储逻辑表的物理数据库。示例:
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
name
:定义数据节点的名字,这个名字需要唯一。上边在table标签上用这个名字来建立表与分片对应的关系
dataHost
:定义该分片属于哪个数据库实例,属性与dataHost标签上定义的name对应
database
:定义该分片属于数据库实例上的具体库
- dataHost标签
这个标签直接定义了具体数据库实例,读写分离配置和心跳语句。示例:
<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="hostM1" url="jdbc:mysql://localhost:3306" user="root" password="root">
<readHost host="hostS1" url="jdbc:mysql://localhost:3306" user="root" password="123456"/>
</writeHost>
</dataHost>
name
:唯一标示dataHost标签,供上层使用,必须唯一
maxCon
:指定每个读写实例连接池的最大连接
minCon
:指定每个读写实例连接池的最小连接,初始化连接池的大小
balance
:指定读写分离的负载均衡类型,目前有4种类型
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不负担读压力(1.4 新增)
writeType
:指定写实例的负载均衡类型,目前有4种类型
-1:表示不自动切换
0:所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个writeHost。重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties
1:所有写操作都随机的发送到配置的writeHost,1.5 以后废弃不推荐使用
2:基于MySQL主从同步的状态决定是否切换(1.4 新增)
dbType
:指定后端连接的数据库类型
dbDriver
:指定连接后端数据库使用的驱动,目前可选的值有 native 和 jdbc
switchType
:指定主从切换的方式
-1:表示不自动切换
1:默认值,自动切换
2:基于MySQL主从同步的状态决定是否切换,心跳检测语句为:show slave status
3:基于MySQL galary cluster 的切换机制(适合集群,1.4.1新增),心跳检测语句为:show status like ‘wsrep%’
slaveThreshold
:定义主从复制延时阈值,当 Seconds_Behind_Master > slaveThreshold 时,读写分离筛选器会过滤掉此Slave机器,防止读到很久之前的旧数据。该属性是用于配合writeType属性实现根据主从延时来进行主从切换的
4.1)heartbeat 标签:指明用于和后端数据库进行心跳检查的语句。例如,MySQL可以使用select user(),Oracle可以使用select 1 from dual 等
4.2)writehost 标签及 readHost 标签:这两个标签属性相同,这里一起介绍
host
:标识不同实例名称,一般writeHost名称使用M1作为后缀,readHost则使用S1作为后缀
url
:配置数据库的连接地址,如果dbDriver属性是 native,则一般为address:port这种形式。如果是
jdbc或其他的,则需要特殊指定。
user
:配置数据库用户名
password
:配置数据库密码
weight
:配置某个数据库在 readHost 中作为读节点的权重
usingDecrypt
:指定是否对密码加密,默认为0, 若需要开启则配置为1
server.xml
该配置文件主要是用于配置系统参数、用户信息、访问权限及SQL防火墙和SQL拦截功能等
- system标签
用于配置mycat的系统配置参数。常见的配置参数示例:
<system>
<!-- mycat 服务连接端口 -->
<property name="serverPort">8066</property>
<!-- mycat 服务管理端口 -->
<property name="managerPort">9066</property>
<!-- mycat 服务监听的ip -->
<property name="bindIp">0.0.0.0</property>
<!-- 0为需要密码登陆、1为不需要密码登陆;默认为0,设置为1则需要指定默认账户-->
<property name="nonePasswordLogin">0</property>
<!-- 前端连接的写队列大小 -->
<property name="frontWriteQueueSize">2048</property>
<!-- 设置字符集编码 -->
<property name="charset">utf8</property>
<!-- mycat 的进程数量 -->
<property name="processors">8</property>
<!-- 闲置连接超时时间,单位:毫秒 -->
<property name="idleTimeout">1800000</property>
<!-- 默认最大返回的数据集大小 -->
<property name="defaultMaxLimit">100</property>
<!-- 允许的最大包大小 -->
<property name="maxPacketSize">104857600</property>
<!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。
在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误-->
<property name="ignoreUnknownCommand">0</property>
<property name="useHandshakeV10">1</property>
<property name="removeGraveAccent">1</property>
<!-- 1为开启实时统计、0为关闭 -->
<property name="useSqlStat">0</property>
<!-- 1为开启全加班一致性检测、0为关闭 -->
<property name="useGlobleTableCheck">0</property>
<!-- SQL 执行超时 单位:秒-->
<property name="sqlExecuteTimeout">300</property>
<property name="sequnceHandlerType">1</property>
<!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况-->
<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
<!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->
<property name="subqueryRelationshipCheck">false</property>
<property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
<!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
<property name="processorBufferPoolType">0</property>
<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->
<property name="handleDistributedTransactions">0</property>
<!-- off heap for merge/order/group/limit 1开启;0关闭 -->
<property name="useOffHeapForMerge">0</property>
<!--是否采用zookeeper协调切换 -->
<property name="useZKSwitch">false</property>
<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->
<property name="strictTxIsolation">false</property>
<!-- Mycat连接数据库时使用的隔离级别
1 - 读未提交
2 - 读已提交
3 - 可重复读
4 - 串行化
-->
<property name="txIsolation">2</property>
<property name="useZKSwitch">true</property>
<!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行-->
<property name="parallExecute">0</property>
</system>
- user标签
用于配置mycat的访问用户及权限。示例:
<!-- 用户名,defaultAccount属性是指定当前账户是否为默认账户 -->
<user name="root" defaultAccount="true">
<!-- 密码 -->
<property name="password">123456</property>
<!-- 允许该用户访问的逻辑库 -->
<property name="schemas">TESTDB</property>
<!-- 可配置多个允许访问的逻辑库,使用逗号分隔 -->
<!-- <property name="schemas">db1,db2</property> -->
<!-- 是否只读 -->
<property name="readOnly">false</property>
<!-- 表级 DML 权限配置,check属性表示是否开启该配置 -->
<privileges check="true">
<!-- 特别权限应用的逻辑库 -->
<schema name="TESTDB" dml="0110">
<!--
配置用户对该表的访问权限,dml属性用于指定权限位,
如果table标签没有配置该属性的话,默认取schema标签的dml属性值,
剩余没有配置的其他表默认也是取schema标签的dml属性值
-->
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
</user>
dml属性配置的数字是权限位,分别对应着 insert,update,select,delete 四种权限。例如,当dml的值为0110时,表示拥有 update 和 select 权限,不具有 insert 和 delete 权限。所以权限位为1时代表拥有对应的操作权限,为0时代表没有该操作权限。
rule.xml
该配置文件主要用于配置切分规则的。数据库水平拆分之后,就需要用这个配置文件定义分片算法规则
配置文件中主要有两种标签,tableRule 和 function ,这两个是一一对应的,示例:
<!-- name属性指定分片规则的名称,必须在 rule.xml 文件中是唯一的 -->
<tableRule name="mod-long">
<rule>
<!-- 指定使用表中的哪个列(字段)进行分片 -->
<columns>id</columns>
<!-- 指定表的分片算法,取值为<function>标签的name属性 -->
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<!-- name属性指定分片算法的名称,同样需要是唯一的;class属性指定该算法的具体实现类 -->
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
<property name="count">3</property>
</function>
mycat默认已经内置了很多分片算法,除了使用这些外也可以根据自己的实际需求自定义算法规则。
mycat内置的常用分片算法有:
- PartitionByMod:简单取模,直接通过列值进行取模得出分片位置。适用于整数类型的列,不能用于非整型的列,且不需要人工干预
- PartitionByHashMod:哈希取模,先将列值进行hash运算之后再取模得出分片位置。适用于非整型的列,且不需要人工干预
- PartitionByFileMap:分片枚举,根据枚举值对数据进行分片。适用于需要人工指定某些数据到哪个分片下。例如在异地多活的场景中通过地区id进行数据分片的场景。
- PartitionByPrefixPattern:字符串范围取模,根据长字符串的前面几位进行取模分片
加密明文密码(可选)
在mycat中,不论是 server.xml 配置文件中的mycat登录密码,还是 schema.xml 配置文件的writeHost和readHost标签的连接密码,都是以明文显示的,只要查看这两个配置文件,就可以知道密码,这种方式可能会存在安全隐患。
所以mycat提供了一个工具用于加密明文密码,该工具在一个jar包内,可使用如下命令对密码进行加密,执行成功后会得到一个加密后的字符串,然后替换配置文件中的明文密码即可
java -cp lib/Mycat-server-1.6.7.6-release.jar io.mycat.util.DecryptUtil 0:root:123456
命令解释:
0:user:password 是加密字符串,有两种格式,示例
0:root:123456
:代表对mycat用户名root明文密码123456的账户进行加密,修改配置文件示例
<user name="root" defaultAccount="true">
<property name="usingDecrypt">1</property>
<property name="password">d6D+pOmkuUoY09p4/aivwMsScLa7zfjIwAxvkEhr3v7en06mEXoX9DTTjQNug5CfvGf7Wy9oLcthYI3yLMSjIg==</property>
<property name="schemas">TESTDB</property>
</user>
1:hostM1:root:123456
:代表对 schema.xml 配置文件中dataHost标签下数据库连接加密,修改示例
<writeHost host="hostM1" url="localhost:3306" user="root" password="BpkNIjF7LfzS1C76HT7B1bJgmGIDtPihqIvHBlC92L1IFqsMfoJEMk1EkxSzjasWB4GWoUcODYO4AaJstdAp5w==" usingDecrypt="1">
</writeHost>
MyCat读写分离
Mycat的读写分离是创建在Mysql的主从复制的基础上的
修改配置文件 schema.xml
[root@mycat ~]# cd mycat/
[root@mycat mycat]# vim conf/schema.xml
一、设置balance="1"与writeType=“0”
Balance参数设置:
修改的balance属性,经过此属性配置读写分离的类型负载均衡类型,目前的取值有4 种:
balance=“0”,不开启读写分离机制, 全部读操做都发送到当前可用的 writeHost 上。
balance=“1”,所有的 readHost与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1, M2->S2,而且M1与M2 互为主备),正常状况下, M2,S1,S2 都参与 select 语句的负载均衡。
balance=“2”,全部读操做都随机的在 writeHost、 readhost 上分发。
balance=“3”,全部读请求随机的分发到 readhost 执行,writerHost 不负担读压力
WriteType参数设置:
writeType=“0”, 全部写操做都发送到可用的writeHost上。
writeType=“1”,全部写操做都随机的发送到readHost。
writeType=“2”,全部写操做都随机的在writeHost、readhost分上发。
“readHost是从属于writeHost的,即意味着它从那个writeHost获取同步数据,所以,当它所属的writeHost宕机了,则它也不会再参与到读写分离中来,即“不工做了”,这是由于此时,它的数据已经“不可靠”了。基于这个考虑,目前mycat1.3和1.4版本中,若想支持MySQL一主一从的标准配置,而且在主节点宕机的状况下,从节点还能读取数据,则须要在Mycat里配置为两个writeHost并设置banlance=1。”
二、设置switchType=“2” 与slaveThreshold=“100”
switchType 目前有三种选择:
-1:表示不自动切换
1 :默认值,自动切换
2 :基于MySQL主从同步的状态决定是否切换
“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主从复制时延。
三、修改配置文件,启动程序
[root@mycat mycat]# vim conf/schema.xml
①控制台启动:mycat/bin 目录下执行 ./mycat console
②后台启动:mycat/bin 目录下./mycat start
为了能第一时间看到启动日志,方便定位问题,选择①控制台启动。
[root@mycat mycat]# ./bin/mycat console
登陆后台管理窗口 此登陆方式用于管理维护 Mycat
[root@mycat ~]# mysql -umycat -p123456 -h 192.168.1.3 -P8066
四、验证读写分离 my.cnf binlog_format=STATEMENT
(1) 在写主机插入数据:
[root@mysql02 ~]# mysql -uroot -p123.com
mysql> use test1;
mysql> select * from tab1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.01 sec)
mysql> insert into tab1 values(4);
Query OK, 1 row affected (0.02 sec)
mysql> select * from tab1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.00 sec)
主从主机数据不一致了
[root@mysql01 ~]# mysql -uroot -p123.com
mysql> use test1
mysql> select * from tab1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
(2) 在Mycat里查询:
mysql> use TESTDB
mysql> select * from tab1;
±-----+
| id |
±-----+
| 1 |
| 2 |
| 3 |
| 4 |
±-----+
4 rows in set (0.00 sec)
垂直拆分–分库
一个数据库由不少表的构成,每一个表对应着不一样的业务,垂直切分是指按照业务将表进行分类, 分布到不一样 的数据库上面,这样也就将数据或者说压力分担到不一样的库
如何划分表分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联关系的表能够分到不一样的库里。
这个案例中须要一台新的mysql来进行分库操做。
主机 | 操做系统 | IP地址 |
---|---|---|
mysql01(master01) | CentOS 7.3 | 192.168.1.1 |
mysql02(slave) | CentOS 7.3 | 192.168.1.8 |
mycat | CentOS 7.3 | 192.168.1.3 |
mysql03(master02) | CentOS 7.3 | 192.168.1.9 |
先将上一个案例插入的数据删除
[root@mysql02 ~]# mysql -uroot -p123.com
mysql> delete from tab1 where id=4;
Query OK, 1 row affected (0.01 sec)
mysql> select * from tab1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
[root@mycat ~]# mysql -umycat -p123456 -h 192.168.1.3 -P8066
mysql> use TESTDB
mysql> select * from tab1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
以下四个表:
客户表分在一个数据库,另外三张都须要关联查询,分在另一个数据库。
#客户表 rows:20万
CREATE TABLE customer(
id INT AUTO_INCREMENT,
NAME VARCHAR(200),
PRIMARY KEY(id)
);
#订单表 rows:600万
CREATE TABLE orders(
id INT AUTO_INCREMENT,
order_type INT,
customer_id INT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
#订单详细表 rows:600万
CREATE TABLE orders_detail(
id INT AUTO_INCREMENT,
detail VARCHAR(2000),
order_id INT,
PRIMARY KEY(id)
);
#订单状态字典表 rows:20
CREATE TABLE dict_order_type(
id INT AUTO_INCREMENT,
order_type VARCHAR(200),
PRIMARY KEY(id)
);
实现分库:
一、 修改 schema 配置文件
[root@mycat mycat]# vim 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">
<table name="customer" dataNode="dn2"> </table>
</schema>
<dataNode name="dn1" dataHost="host1" database="test1" />
<dataNode name="dn2" dataHost="host2" database="test1" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.1.1:3306" user="root"
password="123.com">
<readHost host="hostS2" url="192.168.1.8:3306" user="root" password="123.com" />
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="192.168.1.9:3306" user="root"
password="123.com">
</writeHost>
</dataHost>
</mycat:schema>
受权MyCat远程登录mysql03
[root@mysql03 ~]# mysql -uroot -p123.com
mysql> grant all on *.* to root@'192.168.1.%' identified by '123.com';
由于在配置文件中逻辑库所对应的真实库mysql03中并无,须要在mysql03中建立
mysql> create database test1;
二、 重启mycat,访问MyCat,建立表
[root@mycat mycat]# mysql -umycat -p123456 -h 192.168.1.3 -P8066
mysql> use TESTDB
mysql> CREATE TABLE customer(
-> id INT AUTO_INCREMENT,
-> NAME VARCHAR(200),
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE orders(
-> id INT AUTO_INCREMENT,
-> order_type INT,
-> customer_id INT,
-> amount DECIMAL(10,2),
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE orders_detail(
-> id INT AUTO_INCREMENT,
-> detail VARCHAR(2000),
-> order_id INT,
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE dict_order_type(
-> id INT AUTO_INCREMENT,
-> order_type VARCHAR(200),
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> show tables;
+----------------- +
| Tables_in_test1 |
+----------------- +
| customer |
| dict_order_type |
| orders |
| orders_detail |
| tab1 |
+----------------- +
5 rows in set (0.05 sec)
使用mysql01查看表:
mysql> show tables;
+----------------- +
| Tables_in_test1 |
+----------------- +
| dict_order_type |
| orders |
| orders_detail |
| tab1 |
+----------------- +
4 rows in set (0.00 sec)
使用mysql03查看表:
mysql> use test1;
mysql> show tables;
+----------------- +
| Tables_in_test1 |
+----------------- +
| customer |
+----------------- +
1 row in set (0.00 sec)
查看结构
mysql> desc customer;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| NAME | varchar(200) | YES | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
水平拆分–分表
相对于垂直拆分,水平拆分不是将表作分类,而是按照某个字段的某种规则来分散到多个库之中, 每一个表中 包含一部分数据。简单来讲,咱们能够将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其余的数据库中。
实现分表 选择要拆分的表 MySQL 单表存储数据条数是有瓶颈的,单表达到 1000 万条数据就达到了瓶颈,会影响查询效率,须要进行水平拆分(分表)进行优化。 例如:上一案例的 orders、orders_detail 都已经达到600 万行数据,须要进行分表优化。 分表字段以 orders 表为例,能够根据不一样自字段进行分表
编号 | 分表字段 | 效果 |
---|---|---|
1 | id(键、或建立时间) | 查询订单注重时效,历史订单被查询的次数少,如此分片会形成一个节点访问多,一个访问少,不平均。 |
2 | customer_id(客户ID) | 根据客户 id 去分,两个节点访问平均,一个客户的全部订单都在同一个节点 |
一、修改配置文件 schema.xml
[root@mycat mycat]# vim 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">
<table name="customer" dataNode="dn2"> </table>
<table name="orders" dataNode="dn1,dn2" rule="mod_rule">
<childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
</schema>
<dataNode name="dn1" dataHost="host1" database="test1" />
<dataNode name="dn2" dataHost="host2" database="test1" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="192.168.1.1:3306" user="root"
password="123.com">
<readHost host="hostS2" url="192.168.1.8:3306" user="root" password="123.com" />
</writeHost>
</dataHost>
<dataHost name="host2" maxCon="1000" minCon="10" balance="0"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM2" url="192.168.1.9:3306" user="root"
password="123.com">
</writeHost>
</dataHost>
二、修改配置文件rule.xml
[root@mycat mycat]# vim conf/rule.xml
#在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id,
#还有选择分片算法 mod-long(对字段求模运算) , customer_id 对两个节点求模,根据结果分片
<tableRule name="mod_rule">
<rule>
<columns>customer_id</columns>
<algorithm>mod-long</algorithm>
</rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">2</property>
#配置算法 mod-long 参数 count 为 2,两个节点
</function>
三、由于在dn2(mysql03:192.168.1.9)上并无orders表,在数据节点dn2上建立orders表,并重启mycat服务
[root@mysql03 ~]# mysql -uroot -p123.com
mysql> use test1;
mysql> CREATE TABLE orders(
-> id INT AUTO_INCREMENT,
-> order_type INT,
-> customer_id INT,
-> amount DECIMAL(10,2),
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.00 sec)
四、在MyCat中向orders表插入数据,insert字段不能省略
[root@mycat mycat]# mysql -umycat -p123456 -P8066 -h192.168.1.3
mysql> use TESTDB
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES (1,101,100,100100);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES(2,101,100,100300);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES(3,101,101,120000);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES(4,101,101,103000);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES(5,102,101,100400);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO orders(id,order_type,customer_id,amount) VALUES(6,102,100,100020);
Query OK, 1 row affected (0.01 sec)
#在mycat、dn一、dn2中查看orders表数据,分表成功
dn1(mysql01:192.168.1.1):
[root@mysql01 ~]# mysql -uroot -p123.com
mysql> use test1;
mysql> select * from orders;
+----+------------+-------------+-----------+
| id | order_type | customer_id | amount |
+----+------------+-------------+-----------+
| 2 | 101 | 100 | 100300.00 |
| 4 | 101 | 101 | 103000.00 |
| 6 | 102 | 100 | 100020.00 |
+----+------------+-------------+-----------+
3 rows in set (0.00 sec)
dn2(mysql03:192.168.1.9):
[root@mysql03 ~]# mysql -uroot -p123.com
mysql> use test1;
mysql> select * from orders;
+----+------------+-------------+-----------+
| id | order_type | customer_id | amount |
+----+------------+-------------+-----------+
| 1 | 101 | 100 | 100100.00 |
| 3 | 101 | 101 | 120000.00 |
| 5 | 102 | 101 | 100400.00 |
+----+------------+-------------+-----------+
3 rows in set (0.00 sec)
在dn2(mysql03:192.168.1.9) 建立 orders_detail 表
mysql> CREATE TABLE orders_detail(
-> id INT AUTO_INCREMENT,
-> detail VARCHAR(2000),
-> order_id INT,
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.02 sec)
重启 Mycat 访问 Mycat 向 orders_detail 表插入数据
[root@mycat mycat]# mysql -umycat -p123456 -h 192.168.1.3 -P8066
mysql> use TESTDB
mysql> insert into orders_detail(id,detail,order_id) values (1,'detail',1);
Query OK, 1 row affected (0.03 sec)
mysql> insert into orders_detail(id,detail,order_id) values (2,'detail',2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into orders_detail(id,detail,order_id) values (3,'detail',3);
Query OK, 1 row affected (0.01 sec)
mysql> insert into orders_detail(id,detail,order_id) values (4,'detail',4);
Query OK, 1 row affected (0.01 sec)
mysql> insert into orders_detail(id,detail,order_id) values (5,'detail',5);
Query OK, 1 row affected (0.00 sec)
mysql> insert into orders_detail(id,detail,order_id) values (6,'detail',6);
Query OK, 1 row affected (0.01 sec)
mysql> select o.*,od.detail from orders as o inner join orders_detail as od on o.id=od.order_id;
+----+------------+-------------+-----------+--------+
| id | order_type | customer_id | amount | detail |
+----+------------+-------------+-----------+--------+
| 1 | 101 | 100 | 100100.00 | detail |
| 3 | 101 | 101 | 120000.00 | detail |
| 5 | 102 | 101 | 100400.00 | detail |
| 2 | 101 | 100 | 100300.00 | detail |
| 4 | 101 | 101 | 103000.00 | detail |
| 6 | 102 | 100 | 100020.00 | detail |
+----+------------+-------------+-----------+--------+
6 rows in set (0.03 sec)
使用dn一、dn2查看:
mysql> select * from orders_detail;
mysql> select * from orders_detail;
6、全局表
在分片的状况下,当业务表由于规模而进行分片之后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,考虑到字典表具备如下几个特性:
变更不频繁
数据量整体变化不大
数据规模不大,不多有超过数十万条记录
鉴于此, Mycat 定义了一种特殊的表,称之为“全局表”,全局表具备如下特性:
全局表的插入、更新操做会实时在全部节点上执行,保持各个分片的数据一致性
全局表的查询操做,只从一个节点获取
全局表能够跟任何一个表进行 JOIN 操做 将字典表或者符合字典表特性的一些表定义为全局表,则从另一个方面,很好的解决了数据 JOIN 的难题。
经过全局表+基于 E-R 关系的分片策略, Mycat 能够知足 80%以上的企业应用开发
[root@mycat mycat]# vim conf/schema.xml
<table name="dict_order_type" dataNode="dn1,dn2" type="global"> </table>
在dn2(mysql03:192.168.1.9)建立dict_order_type表
mysql> CREATE TABLE dict_order_type(
-> id INT AUTO_INCREMENT,
-> order_type VARCHAR(200),
-> PRIMARY KEY(id)
-> );
Query OK, 0 rows affected (0.01 sec)
重启MyCat,访问MyCat向dict_order_type表插入数据
[root@mycat mycat]# mysql -umycat -p123456 -h 192.168.1.3 -P8066
mysql> use TESTDB
mysql> insert into dict_order_type (id,order_type) values (101,'type1');
Query OK, 1 row affected (0.03 sec)
mysql> insert into dict_order_type (id,order_type) values (102,'type2');
Query OK, 1 row affected (0.01 sec)
mysql> select * from dict_order_type;
+-----+------------+
| id | order_type |
+-----+------------+
| 101 | type1 |
| 102 | type2 |
+-----+------------+
2 rows in set (0.03 sec)
经常使用分片规则
取模:此规则为对分片字段求摸运算。 也是水平分表最经常使用规则。 5.1 配置分表中, orders 表采用了此规则。 分片枚举:经过在配置文件中配置可能的枚举 id,本身配置分片,本规则适用于特定的场景,好比有些业务 须要按照省份或区县来作保存,而全国省份区县固定的,这类业务使用本条规则。
来源
MySQL中间件之MyCat
详解Mycat核心概念和名词
mycat的介绍及使用
什么是Mycat?为什么要使用MyCat?