MySQL高可用解决方案(5)数据库分库分表中间件Mycat

TangLu MySQL 2021-10-11 2340 0

一、为什么需要分库分表

MySQL的拆分需求主要来自性能、容量、隔离三大问题

· 性能:写请求相应时间增长、DDL时间太长、连接数过多

· 容量:单库备份时间超过1小时,数据库容量大于1T

· 隔离:保证核心业务稳定性,日志表剥离,边缘业务拆分


当单表数据量达到千万级别之后,即使添加从库、优化索引,对于性能的提升也是没有太大作用了。这个时候就要考虑使用分库分表来解决单库或单表的问题。当单库数据量太大,服务器磁盘空间和IO都是瓶颈,当单表数据量太大则会影响数据的增删改查速度。分库分表分为了垂直拆分和水平拆分两种方式,通常先做垂直切分,再做水平切分。

· 垂直拆分(通常用来分库):根据业务的特性将关联程度不同的表放在不同的库中,和微服务思想类似,将大的单体系统解耦。比如一个商城系统的库中会存在用户表、订单表等,可以将这些表分到不同的库中,降低单库的查询压力、连接数压力

· 水平拆分(通常用来分表):根据一定的规则把表数据拆分成多份存放在不同的表中,可以按照时间或者自增id进行拆分,这些表的并集才是一份完整的数据。比如双十一时期天猫商城订单表会存在数亿条数据,如果全部存在一张表里,一个是服务器硬盘压力会过大,另一个是单表查询性能也会很差,对这些大表拆分后可以控制每张表的数量。


二、MySQL分库分表方法

MySQL分库分表分为服务端实现和客户端实现两种方式。服务端的实现方法有常见的Mycat、360 Atlas、淘宝TDDL、MySQL官方的mysql-router等。其中的Mycat基于阿里Cobar二次开发(Cobar已经停止维护),覆盖了其他竞品的特点,不依赖代码就可以实现数据库分布式、读写分离、水平拆分(分库分表)、基于心跳实现高可用一体,相对更有优势。客户端的实现方式则是ShadingJDBC,通常来说客户端方法比服务端方法更适合处理复杂的业务逻辑。


三、Mycat部署

1、安装jdk环境,略


2、通过mycat中文官网(http://www.mycat.org.cn)下载二进制安装包,然后解压到合适的目录

tar zxf Mycat-server-1.6.7.4.tar.gz -C /usr/local/mycat/


3、mycat配置文件

· wrapper.conf——该配置文件主要用于配置java的附加参数,用于性能调优,比如限制mycat消耗的最大内存

vi wrapper.conf
# Java Additional Parameters
#wrapper.java.additional.1=
wrapper.java.additional.1=-DMYCAT_HOME=.
wrapper.java.additional.2=-server
wrapper.java.additional.3=-XX:MaxPermSize=64M
wrapper.java.additional.4=-XX:+AggressiveOpts
wrapper.java.additional.5=-XX:MaxDirectMemorySize=2G
wrapper.java.additional.6=-Dcom.sun.management.jmxremote
wrapper.java.additional.7=-Dcom.sun.management.jmxremote.port=1984
wrapper.java.additional.8=-Dcom.sun.management.jmxremote.authenticate=false
wrapper.java.additional.9=-Dcom.sun.management.jmxremote.ssl=false
wrapper.java.additional.10=-Xmx4G
wrapper.java.additional.11=-Xms1G


· server.xml——该配置文件主要用于修改mycat服务和mycat用户、防火墙信息

<user name="root" defaultAccount="true">  # user标签用于配置用户信息,定义mycat服务所使用的用户和权限
  <property name="password">123456</property>  #mycat用户密码  
  <property name="schemas">TESTDB</property> #mycat用户拥有哪些逻辑库的权限
  #<property name="defaultSchema">TESTDB</property>  #mycat用户默认使用的库
  #<property name="readOnly">true</property>  #是否为只读权限
    #<privileges check="true">  #打开权限检查功能
      #<schema name="TESTDB" dml="0110" >  #这的0110四位数代表增删改善4个权限,1为开
        #<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>


· schema.xml——用于配置mycat的逻辑库、需要拆分的库表、后端数据库节点信息等,逻辑库就是给连接上的客户端所展示的库,这些展现出来的库后端还有真实节点。这是因为做了分表后这些表在不同的实例上,为了让它们看上去是一个整体,通过逻辑库提供统一访问入口(有点像是VIP的概念)

#定义逻辑数据库和后端数据库信息,类似Nginx的反向代理配置
<schema name="linuxe" checkSQLschema="false" sqlMaxLimit="10000" dataNode="linuxe_db">  #访问逻辑库linuxe实际访问的是linuxe_db节点
  <table name="t_insure_0" primaryKey="id" dataNode="node1,node2,node3" rule="mod-long" />  #逻辑表存放节点(可以是多个节点)及分表规则
  <table name="t_insure_1" primaryKey="id" dataNode="node1,node2,node3" rule="mod-long" />  
  <table name="config" primaryKey="id" type="global" dataNode="node1,node2,node3"/>  #type=global代表此表为全局表,所有节点都存在该表,一般用于存放公用配置信息,数据量都很小,不需要切分,每个分片都存一份随机读取,避免跨库join。
</schema>

#定义dataNode数据节点信息,通过dataHost指向后端的database。由于后端可能是主从架构,所以dataHost依然是一个逻辑单位
<dataNode name="node1" dataHost="ha_1" database="linuxe" />  #请求node1这个dataNode时转发到ha_1节点上的linuxe库,这个库是真实库
<dataNode name="node2" dataHost="ha_2" database="linuxe" />  
<dataNode name="node3" dataHost="ha_3" database="linuxe" />  

#定义dataHost数据主机信息,依然是一个逻辑单位,一个dataHost可以是1主1从两台机器,实现读写分离
#balance:指定负载均衡类型,0=不开启读写分离,读操作可以发送到writeHost上,2=随机分发读请求,3=读请求全在readHost
#writeType:0代表所有写操作都发送到配置的第一个writeHost,适用于多主结构;1是随机发送到writeHost,不推荐;
#switchType:主库故障时是否自动切换,-1为不切换,默认1是自动切换,2是根据主从同步的状态来决定是否切换
<dataHost name="ha_1" maxCon="1000" minCon="20" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="-1"  slaveThreshold="100">
  <heartbeat>select user()</heartbeat>  #心跳检测,如果节点异常就不选择该节点
  <writeHost host="db1" url="mydb.ha.com:3306" user="root" password="123456">
    <readHost host="db2" url="mydb.ha.com:3306" user="user" password="123456">   #读节点是包含在写节点中的,写节点挂的话读节点也会失效
  </writeHost>
</dataHost>

<dataHost name="ha_2" maxCon="900" minCon="5" balance="0"
  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
  <heartbeat>select user()</heartbeat>
  <writeHost host="hostM2" url="mydb-history.ha.com:3306" user="root" password="123456"></writeHost>
</dataHost>


mycat1.jpg


· rule.xml——定制分片策略。mycat已经内置了很多策略可以直接使用。常用分片策略介绍:

auto-sharding-long按照范围进行分片例如当ID列每增长1000W就自动进行一个分片,但是存在热点数据问题,因为用户通常都是查当前数据,不关注历史信息

mod-long:采用取模算法进行分片

sharding-by-intfile:枚举分片,例如按照省市区域对数据进行拆分

sharding-by-date:按时间进行分片

#定义了一个mod-long的规则
<tableRule name="mod-long">  
  <rule>
    <columns>id</columns>  #根据id列进行拆分
    <algorithm>mod-long</algorithm>  #调用的函数
  </rule>
</tableRule>

# mod-long 函数信息
 <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
 <!-- how many data nodes -->
 <property name="count">3</property>  #节点数量
 </function>



4、启动Mycat服务并进行连接测试,默认监听8066端口

bin/mycat start
mysql -uroot -p123456 -P8066  #使用server.xml中定义的用户信息



四、Mycat全局序列

分库分表后数据库的自增主键已经无法保证全局唯一,为了避免主键冲突问题,除了使用JAVA生成全局系列,还可以通过Mycat提供的全局序列配置来实现。全局序列虽然可以配置为本地文件,但是存在宕机后无法读取的风险,所以推荐写到数据库中,每次会批量给到Mycat一定数量的序列号,避免每次都申请产生额外的IO开销。创建全局序列表和函数的方式可以使用官方提供好的SQL来操作。

评论