什么是Mycat?
Mycat是数据库中间件
因为放在数据库和应用程序中间,所以叫中间件。是不是觉得有点多余,直接连接数据库不香么?
为什么要使用MyCat?
1.可以应用程序连接到多个数据库
2.缓解单个数据库压力,提升了数据库的访问量与并发量
3.实现了应用程序的高可用,数据库的高可用。
当然可能会有疑问,各个数据库的数据不一致了怎么办,主要mysql,oracle都有主从复制机制,不做详细介绍。
MyCat能干什么呢?
1.读写分离
配置mycat 的schema.xml 文件,大致如下
<dataNode name="dn1" dataHost="localhost1" database="db03" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="pwd">
<readHost host="hostS1" url="127.0.0.2:3306" user="root" password="pwd" />
</writeHost>
<writeHost host="hostM2" url="127.0.0.3:3306" user="root" password="pwd">
<readHost host="hostS2" url="127.0.0.4:3306" user="root" password="pwd" />
</writeHost>
</dataHost>
balance :
负载均衡类型, 目前取值有4种:
balance="0" : 不开启读写分离机制 , 所有读操作都发送到当前可用的writeHost上.
balance="1" : 全部的readHost 与 stand by writeHost (备用的writeHost) 都参与 select 语句的负载均衡,
简而言之,就是采用双主双从模式(M1 --> S1 , M2 --> S2, 正常情况下, M2,S1,S2 都参与 select 语句的负载均衡。);
balance="2" : 所有的读写操作都随机在writeHost , readHost上分发
balance="3" : 所有的读请求随机分发到writeHost对应的readHost上执行, writeHost不负担 读压力 ;balance=3 只在MyCat1.4 之后生效 .
writeType
一般配置0
writeType=“0”, 所有写操作都发送到可用的writeHost上。
writeType=“1”,所有写操作都随机的发送到readHost。
writeType=“2”,所有写操作都随机的在writeHost、readhost分上发。
switchType
表示不自动切换: -1
默认值,自动切换: 1 如果第一个主节点宕机后,Mycat会进行3次心跳检测,如果3次都没有响应,则会自动切换到第二个主节点,并且会更新/conf/dnindex.properties文件的主节点信息 localhost1=0 表示第一个节点.该文件不要随意修改否则会出现大问题
switchType=2,基于MySQL主从同步的状态决定是否切换,心跳语句为show slave status。此时设置slaveThreshold=100表示从节点落后主库100秒就会剔除这个从节点
使用中遇到的问题
1.主从复制延迟高,因为数据库设置了触发器,导致更新数据慢,同时也引起了主从复制延时高的问题
2.刚插入数据,立刻查询查询不到,特别是mysql设置了自增长主键,必须要查一次的时候
解决方案
1.强制走主库查询,可通过注解的形式 /*#mycat:db_type=master*/
注解支持的’!‘不被 mysql 单库兼容,
注解支持的’#'不被 mybatis 兼容
/*#mycat:db_type=master*/ select * from travelrecord
/*!mycat:db_type=slave*/ select * from travelrecord
其他注解
1. Mycat 端执行存储创建表或存储过程为:
存储过程:
/*!mycat: sql=select 1 from user*/ 存储过程sql ;
表:
/*!mycat: sql= select 1 from user */ 建表sql;
插入数据:
/*!mycat: sql= select 1 from user */插入sql;
注意注解中语句是节点的表请替换成自己表如 select 1 from user ,注解内语句查出来的数据在哪个分片,数据在那个节点往哪个节点执行后面的sql
2. 读写分离
配置了 Mycat 读写分离后,默认查询都会从读节点获取数据,但是有些场景需要获取实时数据,如果从读节点获取数据可能因延时而无法实现实时,Mycat 支持通过注解/*balance*/来强制从写节点查询数据:
a. 事务内的 SQL,默认走写节点,以注解/*balance*/开头,则会根据 schema.xml 的 dataHost 标签属性的
balance=“1”或“2”去获取节点
b. 非事务内的 SQL,开启读写分离默认根据 balance=“1”或“2”去获取,以注解/*balance*/开头则会走写节
点解决部分已经开启读写分离,但是需要强一致性数据实时获取的场景走写节点
/*balance*/ select * from user;
4. 多表 ShareJoin(分库分表的时候用,实际我也没有用过)
/*!mycat:catlet=demo.catlets.ShareJoin */ select a.*,b.id, b.name as tit from customer a,company b on
a.company_id=b.id;
4.读写分离数据源选择
/*!mycat:db_type=master*/ select * from user
/*!mycat:db_type=slave*/ select * from user
/*#mycat:db_type=master*/ select * from user
/*#mycat:db_type=slave*/ select * from user
5. 多租户支持
通过注解方式在配置多个 schema 情况下,指定走哪个配置的 schema。
web 部分修改:
a.在用户登录时,在线程变量(ThreadLocal)中记录租户的 id
b.修改 jdbc 的实现:在提交 sql 时,从 ThreadLocal 中获取租户 id, 添加 sql 注释,把租户的 schema放到注释中。例如:/*!mycat : schema = test_01 */ sql ;
在 db 前面建立 proxy 层,代理所有 web 过来的数据库请求。proxy 层是用 mycat 实现的,web 提交的 sql 过来时在注释中指定 schema, proxy 层根据指定的 schema 转发 sql 请求。
/*!mycat : schema = test_01 */ sql ;
2.数据分片
两个典型例子
垂直拆分——分库
一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,
分布到不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:
分库的原则:有紧密关联关系的表应该在一个库里,相互没有关联关系的表可以分到不同的库里。
垂直拆分的优点:
- 拆分后业务清晰,拆分规则明确
- 系统之间进行整合或扩展很容易
- 按照成本、应用的等级、应用的类型等将表放到不同的机器上便于管理数据
- 维护相对简单
垂直拆分的缺点:
- 部分业务表无法Join,只能单表查询或通过接口方式解决,提高了系统的复杂度
- 受每种业务的不同限制,存在单库性能瓶颈,不易进行数据扩展和提升性能
- 事务处理变复杂
mycat具体如何分库?
编辑myca/conf/schema.xml文件
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="agencyDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<!-- 指定表名到对应dn2节点,其他默认dn1 -->
<table name="user_info" dataNode="dn2" ></table>
<table name="mobile_info" dataNode="dn2" ></table>
</schema>
<!-- 配置了两个节点 -->
<dataNode name="dn1" dataHost="host1" database="testdb" />
<dataNode name="dn2" dataHost="host2" database="testdb" />
<dataHost name="host1" 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="hostM1" url="127.0.0.1:3306" user="root"
password="123456">
</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>
<!-- can have multi write hosts -->
<writeHost host="hostM2" url="127.0.0.2:3306" user="root"
password="123456">
</writeHost>
</dataHost>
</mycat:schema>
所有需要拆分的表都需要在table标签中定义
基于以上拆分的user_info和mobile_info 两个表则可以拆分至host2中
另外一个思路,根据微服务拆分成不同的schema,每个微服务对应一个schema则可
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="agencyDB1" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
</schema>
<schema name="agencyDB2" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn2">
</schema>
<!-- 配置了两个节点 -->
<dataNode name="dn1" dataHost="host1" database="userdb" />
<dataNode name="dn2" dataHost="host2" database="orderdb" />
<dataHost name="host1" 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="hostM1" url="127.0.0.1:3306" user="root"
password="123456">
</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>
<!-- can have multi write hosts -->
<writeHost host="hostM2" url="127.0.0.2:3306" user="root"
password="123456">
</writeHost>
</dataHost>
</mycat:schema>
思考,如果分库后单个表的数据量仍然到达千万级
水平拆分——分表
相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:
水平拆分的优点:
- 单库单表的数据能保持在一定的量级,有助于性能的提高。
- 切分的表结构相同,应用层改造较少,只需要增加路由规则即可。
- 提高了系统的稳定性和负载能力。
水平拆分的缺点:
- 切分后,数据是分散的,跨库join操作难和性能差
- 拆分规则难以抽象
- 分片事务的一致性难以解决
- 数据扩容的难度和维护量极大
mycat实现分表
假设test表已经有1000万数据了。单表达到1000
万条数据就达到了瓶颈,会影响查询效率, 需要进行水平拆分(分表)进行优化。
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="agencyDB1" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
<table name="test" dataNode="dn1,dn2" rule="mod_rule" ></table>
</schema>
<!-- 配置了两个节点 -->
<dataNode name="dn1" dataHost="host1" database="userdb" />
<dataNode name="dn2" dataHost="host2" database="userdb" />
<dataHost name="host1" 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="hostM1" url="127.0.0.1:3306" user="root"
password="123456">
</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>
<!-- can have multi write hosts -->
<writeHost host="hostM2" url="127.0.0.2:3306" user="root"
password="123456">
</writeHost>
</dataHost>
</mycat:schema>
核心配置是table标签,指定分片规则为mod_rule
配置rule.xml
新加tableRule配置
<tableRule name="mod_rule">
<rule>
<columns>id</columns>
<algorithm>od-long</algorithm>
</rule>
</tableRule>
name:指定唯一的名字,用于标识不同的表规则。
columns:指定要拆分的列名字。
algorithm:使用function标签中的name属性,连接表规则和具体路由算法
mod-long规则为配置文件自带的,修改count属性。代表意思为,根据count数取模
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<!-- how many data nodes -->
<property name="count">3</property>
</function>
根据以上规则假设数据id是1-10,那么id是 1,4,7,10 的数据在一个节点,id是2,5,8 的数据在一个节点,id是3,6,9的数据在一个节点
这是最常用的取模运算分片规则
枚举规则
通过在配置文件中配置可能的枚举id
,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照国内国际类型不同保存订单信息
修改schema.xml文件
<table name="orders" dataNode="dn1,dn2" rule="sharding_by_type" ></table>
修改rule.xml文件
<tableRule name="sharding_by_type">
<rule>
<columns>type</columns>
<algorithm>hash-int</algorithm>
</rule>
</tableRule>
columns
:分片字段algorithm
:分片函数
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">hash-int.txt</property>
<property name="type">1</property>
<property name="defaultNode">0</property>
</function>
mapFile
:标识配置文件名称type
:0为int型、非0为String
defaultNode
:默认节点。小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点,设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错。
修改配置文件hash-int.txt文件
CHN=0
INT=1
范围分片
此分片适用于提前规划好分片字段某个范围属于哪个分片。
修改schema.xml文件
<table name="orders" dataNode="dn1,dn2" rule="sharding_by_range" ></table>
修改rule.xml文件
<tableRule name="sharding_by_range">
<rule>
<columns>id</columns>
<algorithm>range-long</algorithm>
</rule>
</tableRule>
columns
:分片字段algorithm
:分片函数
<function name="ranage-long" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">range-long.txt</property>
<property name="defaultNode">0</property>
</function>
mapFile
:标识配置文件名称defaultNode
:默认节点。小于 0 表示不设置默认节点,大于等于 0 表示设置默认节点,设置默认节点如果碰到不识别的枚举值,就让它路由到默认节点,如不设置不识别就报错。
修改配置文件range-long.txt文件
0-100=0
101-200=1
以上将id是0-100 的数据分到一个节点,101-200的数据分到一个节点
日期分片
配置步骤:
修改配置文件schema.xml
<table name="login_info" dataNode="dn1,dn2" rule="sharding_by_date" ></table>
修改配置文件rule.xml
<tableRule name="sharding_by_date">
<rule>
<columns>create_at</columns>
<algorithm>shardingByDate</algorithm>
</rule>
</tableRule>
columns:分片字段
algorithm:分片函数
<function name="shardingByDate" class="io.mycat.route.function.PartitionByDate">
<property name="dateFormat">yyyy-MM-dd</property>
<property name="sBeginDate">2022-01-01</property>
<property name="sEndDate">2025-01-01</property>
<property name="sPartionDay">2</property>
</function>
dateFormat :日期格式
sBeginDate :开始日期
sEndDate:结束日期,则代表数据达到了这个日期的分片后循环从开始分片插入,也可以不设置
sPartionDay :分区天数,即默认从开始日期算起,分隔2天一个分区
还有一些其他方法,未深入研究
何时分库分表
非必要不分库,非必要不分表
IO瓶颈
第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询会产生大量的IO,降低查询速度->分库和垂直分表
第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 ->分库
CPU瓶颈
第一种:SQl问题:如SQL中包含join,group by, order by,非索引字段条件查询等,增加CPU运算的操作->SQL优化,建立合适的索引,在业务Service层进行业务计算。
第二种:单表数据量太大,查询时扫描的行太多,SQl效率低,增加CPU运算的操作。->水平分表。
其他分库分表中间件
sharding-jdbc(当当)
TSharding(蘑菇街)
Atlas(奇虎360)
Cobar(阿里巴巴)
Oceanus(58同城)
Vitess(谷歌)
我都没用过,不了解