MySQL主从方案业务连接透明化

1、MySQL主从方案业务层的问题

在之前的文章中,我们提到MySQL一主多从集群模式下,对上层业务系统的访问带来了一些问题。本编文章中我们将深入分析这个问题,并介绍如何对这个问题进行改进。MySQL一主多从集群对上层业务系统带来的主要问题是,上层业务系统需要自行控制本次MySQL数据操作需要访问MySQL集群中的哪个节点。产生这个问题的主要原因,是因为MySQL一主多从集群本身并没有提供现成功能,将集群中的节点打包成统一服务并向外提供

这里写图片描述

在上图所示的MySQL集群中,有一个Master节点负责所有的写事务操作,还有两个Salve节点分别负责订单模块的读操作和用户模块的读操作,而这个架构方案中由于没有中间管理层,所以到底访问哪一个MySQL服务节点的判断工作全部需要由上层业务系统自行判断。那么解决这个问题的思路也就比较清晰了:我们需要通过一些手段自行为业务层的访问增加一个中间层,以减少业务开发人员的维护工作

2、改进方式一:使用Spring套件屏蔽细节

如果您的工程使用了Spring组件,那这个问题可以使用Spring配置问题进行改善。但这个方式只能算是改善问题,不能算作完全解决了问题。这是因为虽然通过Spring配置后,业务开发人员不需要再为“访问哪个数据库节点”操碎了心,但是Spring的配置文件依然是存在于业务系统中,当下层MySQL集群节点发生变化时,业务系统就需要改变配置信息并且重新部署;当MySQL集群现有节点发生故障时,上层业务系统也需要变更配置信息并重新部署。这种配置的方法并不能实现数据访问逻辑的完全脱耦。

下面我们给出一个示例,在这个示例中我们使用spring 3.X 版本 + hibernate 4.X 版本 + c3p0 + MySQL JDBC 实现在业务系统中访问数据库节点的规则配置。

这里写图片描述

如上图所示,我们在业务系统中建立了两个数据源:writeSessionFactory、readSessionFactory,分别负责业务数据的写操作和读操作。当下面的MySQL集群增加新的读节点或者集群中现有节点发生变化时,spring的配置文件也要做相应的配置变化:

  • 写操作涉及的数据源和AOP点配置
<!-- 工程中和读写数据源分离无关的Spring配置信息,在这里就不进行赘述了 -->
......
<!-- 这个数据源连接到maseter节点, 作为写操作的数据源-->
<bean id="writedataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass"><value>${writejdbc.driver}</value></property>
    <property name="jdbcUrl"><value>${writejdbc.url}</value></property>
    <property name="user"><value>${writejdbc.username}</value></property>
    <property name="password"><value>${writejdbc.password}</value></property>
    <property name="minPoolSize"><value>${writec3p0.minPoolSize}</value></property>
    <property name="maxPoolSize"><value>${writec3p0.maxPoolSize}</value></property>
    <property name="initialPoolSize"><value>${writec3p0.initialPoolSize}</value></property>
    <property name="maxIdleTime"><value>${writec3p0.maxIdleTime}</value></property>
    <property name="acquireIncrement"><value>${writec3p0.acquireIncrement}</value></property>
</bean>

<!-- 数据层会话工厂和数据源的映射关系 -->
<bean id="writeSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="writedataSource" />
    <property name="namingStrategy">
        <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${writehibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${writehibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${writehibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${writehibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
        </props>
    </property>
</bean>
<bean id="writetransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="writeSessionFactory" />
</bean>

<!-- 
AOP设置,在templateSSHProject.dao.writeop包或者子包中
涉及以下名字的方法,都要开启事务托管。
并且在抛出任何异常的情况下,spring都要回滚事务
-->
<aop:config> 
    <aop:pointcut id="writedao" expression="execution(* templateSSHProject.dao.writeop..*.* (..))" />
    <aop:advisor advice-ref="writetxAdvice" pointcut-ref="writedao" />
</aop:config>
<tx:advice id="writetxAdvice" transaction-manager="writetransactionManager">
    <tx:attributes>
        <tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="update*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="delete*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="modify*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="create*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="remove*" rollback-for="java.lang.Exception" propagation="REQUIRED" />
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
......

 

  • 读操作涉及的数据源和AOP点配置,和写操作数据源配置类似,各位读者只需要注意不同点
......
<!-- 这个数据源连接到salve节点, 作为读操作的数据源 -->
<bean id="readdataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass"><value>${readjdbc.driver}</value></property>
    <property name="jdbcUrl"><value>${readjdbc.url}</value></property>
    <property name="user"><value>${readjdbc.username}</value></property>
    <property name="password"><value>${readjdbc.password}</value></property>
    <property name="minPoolSize"><value>${readc3p0.minPoolSize}</value></property>
    <property name="maxPoolSize"><value>${readc3p0.maxPoolSize}</value></property>
    <property name="initialPoolSize"><value>${readc3p0.initialPoolSize}</value></property>
    <property name="maxIdleTime"><value>${readc3p0.maxIdleTime}</value></property>
    <property name="acquireIncrement"><value>${readc3p0.acquireIncrement}</value></property>
</bean>

<!-- 数据层会话工厂和数据源的映射关系,基本上和write的设置一致 -->
<bean id="readSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="readdataSource" />
    <property name="namingStrategy">
        <bean class="org.hibernate.cfg.ImprovedNamingStrategy" />
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${readhibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${readhibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${readhibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${readhibernate.hbm2ddl.auto}</prop>
            <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
        </props>
    </property>
</bean>
<bean id="readtransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="readSessionFactory" />
</bean>

<!-- 
AOP设置,和写操作事务不同的点也在这里
这里不需要设置任何方法方法开启事务托管
甚至不需要配置这段AOP切面
-->
<aop:config> 
    <aop:pointcut id="readdao" expression="execution(* templateSSHProject.dao.readop..*.* (..))" />
    <aop:advisor advice-ref="readtxAdvice" pointcut-ref="readdao" />
</aop:config>
<tx:advice id="readtxAdvice" transaction-manager="readtransactionManager">
    <tx:attributes>
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
......

 

  • 要看懂以上的Spring配置信息,首先请确定您接触过Spring组件。以上配置主要使用XML文件定义的方式指定需要使用“写数据源”的方法名,使用AOP切面为这些指定的方法名开启事务托管。在实际使用过程中,各位读者也可以使用Java代码注解的方式在指定的包范围内标记需要开启事务托管的方法。只需要在配置文件中增加一段说明信息“<tx:annotation-driven transaction-manager=”writetransactionManager” proxy-target-class=”false”/>”

  • 这样的读写数据源分离的方式,只会影响业务开发人员在数据层的操作,对显示层和业务逻辑层没有任何影响。这里再给出工程中数据层部分的包结构,这个结构和AOP配置中的切面扫描点“expression”有关。从以下给出的结构图可以看出,在数据层进行的数据读操作和数据写操作是分离的,这样可以避免业务开发人员在编写代码时发生混淆(例如在负责读操作的工程包中进行写操作)

    这里写图片描述

  • 当然针对各位读者自身的业务形态,您也可以将两个数据源合并在一起混合使用,不过这可能会加重业务开发人员的维护工作。将读写操作数据源合并的方式也很简单,基本上不需要更改任何配置,只需要将两个sessionFactory注入到同一个AbstractRelationalDBDAO——为DAO层设置的公共父类。

    ......
    public abstract class AbstractReadRelationalDBDAO ...... {
    
        // AbstractRelationalDBDAO类的其它部分都省略了
        ......
        @Autowired
        @Qualifier("readSessionFactory")
        private SessionFactory readSessionFactory;
    
        @Autowired
        @Qualifier("writeSessionFactory")
        private SessionFactory sessionFactory;
        ......
    }
    ......
  • 本示例实现的Spring配置中只有一个负责写操作数据源和一个负责读操作数据源。那么从理论上讲这种Spring配置方式只能适应一主一从的MySQL集群。当MySQL集群中的节点发生,例如增加了一个Salve从节点,业务工程就会增加一个从节点的数据源配置信息,并且在工程的数据层(DAO层)增加新的代码包。这显然是很有问题的,甚至可以说整个业务工程基本无法维护,这是因为再稳定的MySQL集群也只能保证5个9的系统可用性(即99.999%),另外现在主流的集群思想中本来就是假设集群中的节点随时可能出现问题,而业务系统显然不可能在无法预知的情况下随时改变配置信息并重新部署。

3、改进方式二:透明化中间层

那么有没有什么办法能够解决以上的问题呢?既突破以上Spring配置方式只适应一主一从MySQL集群的瓶颈,又不增加业务开发人员的维护难度,还能适应下层数据集群随时发生的节点故障。当然是有办法的,使用我们已经在负载均衡专题介绍过的LVS,我们可以为MySQL集群中的多个读节点构造一个透明层,使得它们可以作为一个整体,并使用一个统一的访问地址向上层业务系统提供数据读取服务。

这里写图片描述

如果您还不清楚LVS配置方式,可以参见我另外一专题中,专门介绍LVS的几篇文章:《架构设计:负载均衡层设计方案(4)——LVS原理》、《架构设计:负载均衡层设计方案(5)——LVS单节点安装》。需要注意的是,这里选择的负载方案应该工作在网络协议的下层,例如OSI七层模型的链路层或者传输层。这是因为上层系统连接MySQL服务节点主要基于TCP/IP协议而不是基于HTTP协议,例如MySQL的多数客户端软件(MySQL-Front、Navicat等)都使用MySQL原生连接协议,这个协议就是基于TCP/IP协议的,再例如绝大多数Java应用程序连接和调用MySQL操作所基于的JDBC API,也是基于TCP/IP协议。所以这里使用的负载均衡方案不能使用Nginx这样只支持Http协议的组件,而LVS组件可以很好的适应技术需求。

以上的改进方案中,我们只对MySQL集群中的读操作节点进行了改进,但是整个集群还是没有足够的稳定保证。这是因为MySQL集群中写操作节点目前还只有单个节点承载工作,新加入的LVS负载节点也只有单个节点承载工作。如果在生产环境下,以上这些节点出现故障无法工作将导致整个MySQL集群崩溃。进一步的改进方式,就是为集群中的写操作节点和LVS负载节点增加热备方案,如下图所示:

这里写图片描述

  • 上图中我们使用Heartbeat + DRBD第三方组件的方式,为MySQL Master节点复制了一个可以即时切换的处于“准备”状态的备用节点。Heartbeat组件的作用和之前我们介绍过的keepalived组件类似,它用于监控两个(或多个)节点的工作状态,并在满足宕机的判断条件时完成浮动IP的切换和备用服务的启动工作。DRBD组件是一个工作在Linunx系统下的,可以完成实时文件差异化同步的磁盘块映射软件,类似的软件还有RSync。

  • 有了Heartbeat + DRBD第三方组件的支持,就可以保证当MySQL集群中的写操作节点不能提供服务时,另一个等待工作的备份写操作节点能够及时的接过工作任务,并且这个备份节点上的数据库表数据和之前崩溃的写操作节点上的数据库表数据是一致的。这个方案还可以更换成Keepalived + RSync的第三方组件方案。

  • LVS节点的高可用方案,在之前的文章中已经介绍过了。不清楚的读者可以参考我另一篇文章《架构设计:负载均衡层设计方案(7)——LVS + Keepalived + Nginx安装及配置》,只不过文章中的需要被保证高可用性的组件由Nginx替换成了MySQL服务。不过这样的读节点组织方式,也有一些问题:虽然这些读节点通过负载均衡的方式可以分担各自的工作压力,但是这些读操作节点不能按照上层业务的不同,分模块提供独立的、有个性的查询操作服务。

 

4、改进方式三:MyCat数据库中间件

在上文中我们介绍了MySQL读写分离集群的持续优化方式。按照这样的方式,集群中负责读写分离的MySQL节点基本上能够分别实现真对上层业务系统访问的透明化。这样的MySQL集群方式已经可以承载读者遇到的大部分业务系统的结构化数据规模,但整个集群方案还有一些明显的问题:首先就是业务开发人员始终还是需要分配一定的精力去分别管理读操作会话和写操作会话的数据库连接;其次这个集群方案主要解决的是单张数据表数据量不大条件下的MySQL读写性能(千万级数据,还有一个前提是数据表本身拥有符合业务要求的索引结构且数据节点的性能配置合理),如果数据库中单张数据表的规模达到了亿级(甚至以上),那么查询压力集中在一个读节点上也不会再有助于查询性能提升。

所以在必要的情况下,我们还需要继续对上文给出的MySQL集群方案进行改进,让其能够适应更庞大的结构化数据规模。具体的思路就是在保持读写分离方案的基础上,对业务系统中结构化数据量达到或者超过亿级规模的若干张业务表进行拆分工作。本篇文章向读者介绍一个由国人开发并完全免费发布的数据库中间件,MyCat。它不但可以作为之前介绍的读写分离方案的替换方案,更重要的是它还能独立创建和管理数据表的拆分工作。

在实际运行的业务系统中,如果其承载的结构化数据量超过了亿级,那么最可能的情况是数据库中某一张或者某几张数据表的承载规模超过亿级,其它大多数数据表的数据量并不会太大。所以实际工作中架构师和DBA只需要针对这几张数据表和直接关联的数据表设计数据拆分方案

4-1、MyCat基本概念

MyCat是一款数据库中间件,它基于阿里的一款数据库中间件Cobar而研发。“数据库中间件”这在整个专题的文章中都是一个新词语,但从“中间件”这个关键词来看我们至少可以知道:MyCat并不真正处理数据库操作而是作为一个中间层存在于业务系统和真实的数据存储系统之间。

这里写图片描述

上图来源于MyCat官网(http://www.mycat.org.cn/)。是的,MyCat数据库中渐渐最关键的一个动词是“拦截”,它拦截了用户发送过来的 SQL 语句,对 SQL 语句做一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此 SQL 发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。MyCat数据库中间件不只可以作为MySQL(MariaDB)的最佳搭配,还可以提供对ORACLE、DB2、SQL Server、MongoDB的支持。只不过MyCat对这几个关系型数据库的支持是基于JDBC规范,将它们模拟为MySQL,而对MongoDB的支持(Version 1.3+的版本支持)是封装了 MongoDB API 基于JDBC的实现。MyCat最关键的功能特性还包括:

  • 支持单纯的读写分离:只需要进行最简单的配置过程,就可以让MyCat对下层数据存储集群提供读写分离功能的支持,并且提供数据写操作节点的主从切换操作。

  • 支持数据表的拆分功能,包括指定数据表的纵向拆分和横向拆分(又称为垂直拆分和水平拆分)。并且这个拆分过程和拆分后的维护过程对上层开发人员完全透明。

  • 多租户功能:同一个应用的,连接同一个MyCat数据库中间件,如果采用不同的连接用户将会由MyCat将实际操作路由到不同的物理节点上,从而实现多租户功能。

在正式介绍MyCat使用之前,为了便于读者能够更好的理解后续的技术方案,需要首先向读者介绍一些和下文示例有关的MyCat基础定义。如果读者需要深入了解MyCat的工作原理和技术实现,可以到其官网上下载更多资料。

这里写图片描述

数据的拆分方式一般分为两种:横向(水平)和纵向(垂直)。纵向(垂直)拆分方式从技术层面上来说相对简单且便于维护,其基本原理是将原本存在于同一个数据库中的属于不同业务模块的数据表分别拆分到不同的数据库中进行存储(这些数据库可能在同一操作系统上也可能不在同一操作系统上,可能在同一个数据库集群上也可能不在同一个数据库集群上)。纵向(垂直)拆分其操作目标是数据表,要完成这样的拆分动作,技术团队至少应该保证这些业务模块存在极少的耦合度。横向(水平)拆分是指为了限制/减少将某张数据表中的数据规模,按照某种规则将这张数据表拆分成字段相同的多张数据表,并存储到不同的数据库中(这些数据库一般不在同一个操作系统上)。 横向(水平)拆分操作目标是数据表中的数据,其目的是在结构化数据达到一定规模时(达到千万级别),将针对数据表的某一次操作请求的压力分配到若干个服务节点上。可见真正能够进一步解决关系型数据库性能问题的办法,还需要对数据表进行横向(水平)拆分,而MyCat的数据组织结构主要就是为了支持数据表的横向(水平)拆分。

  • 逻辑库(schema):前文中已经提到,MyCat数据库中间件虽然本身不存储任何数据,也不存在任何的数据表结构,更没有任何数据库文件。但是为了描述底层真实数据的协调结构,MyCat数据库中间件存在一些逻辑概念,首先最顶层的逻辑结构就是逻辑库。连接使用MyCat数据库中间件的上层业务系统,实际上并不知道最底层真实数据的组织结构,对于它来说MyCat就是它使用的一个逻辑库。

  • 逻辑表(table)和逻辑节点/分片节点(dataNode):逻辑库中包含多个逻辑表,所谓逻辑表同样没有真实数据,只是为了方便描述底层真实数据的协调结构和向上层使用者屏蔽访问细节。逻辑表对应的逻辑存储位置称为逻辑节点/分片节点,逻辑表和逻辑节点/分片节点是进行数据表横向(水平)拆分的关键,这是因为逻辑表不但可以指定自身的逻辑处理规则,还可以根据逻辑表和逻辑节点/分片节点的对应关系确定数据分发方式。

  • 节点主机(dataHost):逻辑节点最终要对应物理性质的配置,在MyCat中称为节点主机。一个逻辑节点需要对应一个节点主机。在节点主机下技术人员可以设置物理节点,在上图中MyCat节点主机dataHost2下设置了三个MySQL物理节点。通过节点主机配置的功能,技术人员还可以分别设置了这些物理节点的读操作特性和写操作特性。是的,这是MyCat支持读写分离功能的关键配置。

4-2、使用MyCat完成读写分离

作为快速上手也为了继续优化之前文章中介绍的MySQL读写分离方案,本小节我们首先通过MyCat配置一个最简的、单纯的读写分离集群。配置过程的是指也很简单:不需要对逻辑表进行分片,也就是说只需要配置一个分片表和分片节点;只使用读写分离功能,也就是说只需要在这唯一的一个分片节点配置读节点和写节点以及节点健康的检查策略

这里写图片描述

您可以在MyCat官网上下载MyCat的Linux版本,在本文发表时MyCat稳定版本为V 1.6.1。MyCat数据库中间件的工作目录下的conf文件夹中存放着所有需要的配置文件,其中有几个配置文件比较重要,它们是:schema.xml、rule.xml和server.xml。rule.xml描述了逻辑表和逻辑节点/分片节点的关联规则,也即是数据的分片规则;server.xml描述了MyCat服务的访问规则和用户授权规则。最重要的是schema.xml文件,其中描述了上文中我们提到的几个关键概念,逻辑库、逻辑表、分片节点和节点主机。一般来说我们要实现单纯的读写分离集群,只需要对schema.xml文件进行更改:

<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
    <!-- 首先是逻辑库的定义,逻辑库名叫做qiangSchema。其中定义了两个逻辑表 -->
    <schema name="qiangSchema" checkSQLschema="false" sqlMaxLimit="100">
        <!-- 
        逻辑表要和物理表的名字一样,大小写也最好一样。关于type属性的定义后文会详细描述 
        两张逻辑表都对应一个逻辑节点/分片节点——dn1。也就是说两张逻辑表都不做水平拆分
        -->
        <table name="myuser" primaryKey="Id" type="global" dataNode="dn1" />
        <table name="t_user" primaryKey="uid" type="global" dataNode="dn1" />
    </schema>
    <!-- 逻辑节点/分片节点的定义,注意其使用的节点主机定义 -->
    <dataNode name="dn1" dataHost="dataHost" database="qiang" />
    <!-- 节点主机中包括两个物理节点,其中一个是读节点另一个是写节点 -->
    <dataHost name="dataHost" maxCon="1000" minCon="10" balance="1" writeType="0"
        dbType="mysql" dbDriver="native" switchType="2">
        <heartbeat>select user()</heartbeat>
        <!-- MyCat支持一个节点主机下定义多个读节点和多个写节点 -->
        <writeHost host="hostM1" url="192.168.61.140:3306" user="root" password="123456">
            <!-- can have multi read hosts -->
            <readHost host="hostS1" url="192.168.61.141:3306" user="root" password="123456"/>
        </writeHost>
    </dataHost>
</mycat:schema>

 

这里要注意一个关键点,请在由Linux For MyCat控制的各个MySQL节点上设置“lower_case_table_names = 1”,否则使用MyCat的时候会提示找不到表的错误。以上代码段落示意了MyCat中实现的最简单的读写分离集群最关键的配置信息,这里对配置信息进行一些必要的说明(部分说明直接摘自MyCat官方文档):

  • schema:checkSQLschema:当该值设置为 true 时,如果我们执行语句“select * from qiangSchema.travelrecord;”则 MyCat 会把语句修改为“select * from travelrecord;”。即把表示 schema 的字符去掉,避免发送到后端数据库执行时报“(ERROR 1146 (42S02): Table‘qiangSchema.travelrecord’ doesn’t exist)”。

  • schema:sqlMaxLimit:当该值设置为某个数值时。每条执行的 SQL 语句,如果没有加上 limit 语句,MyCat 也会自动的加上所对应的值(适用于MySQL数据库)。

  • table:primaryKey:该逻辑表对应真实表的主键。

  • table:type:该属性定义了逻辑表的类型,目前逻辑表只有“全局表”(global)和”普通表”两种类型。如果是普通表,则不需要设置这个type属性。那么什么叫做全局表呢?数据库拆分的关键在于被拆分的数据表和关联表之间的join操作符,Mycat 中通过数据冗余来解决这类表间的join操作问题,即所有的分片都有一份数据的拷贝,这样的数据表定义为全局表。在数据库中存在的字典性质的的数据表或者变化率趋于0的数据表都应设置为全局表。

  • dataHost:maxCon和dataHost:minCon:分别指定每个读写实例连接池的最大连接数和最小连接数。也就是说,标签内嵌套的 writeHost、readHost 标签都会使用这个属性的值来实例化出连接池的最大连接数。

  • dataHost:balance:这个设置很关键,涉及到节点主机中各个物理节点的读负载模式问题。设置值为0, 不开启读写分离机制,所有读操作都发送到当前可用的writeHost上;设置值为1,全部的readHost与stand by writeHost参与 select 语句的负载均衡;设置为2时,所有读操作都随机的在 writeHost、readhost 上分发;设置为3时,所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost不负担读压力。注意只在 MyCat Version 1.4及以后的版本才支持值为3的设置。

  • dataHost:writeType:该属性涉及节点主机中个物理节点的写负载模式问题。MyCat目前支持最好或者说推荐的设置值就是0。当writeType属性设置为0时,所有写操作发送到配置的第一个writeHost,如果第一个writeHost挂了,则切到还生存的第二个writeHost。重新启动后已切换后的为准,切换记录在配置文件dnindex.properties中。

  • dataHost:dbType和dataHost:dbDriver:dbType属性指定后端连接的数据库类型,目前支持二进制的 mysql 协议,还有其他使用 JDBC 连接的数据库。目前dbType属性可指定的值包括:mysql、maridb、oracle、mongodb、postgresql。dbDriver属性则可以给定连接指定数据库所使用的驱动类型,如果是连接mysql和maridb,则该属性可以设置为native(原生的mysql网络连接协议),否则请指定数据库对应的JDBC驱动实现(需要将符合JDBC 4标准的驱动JAR包放到MyCat安装路径下的lib目录中)。

  • dataHost:switchType:该属性描述多个writeHost的切换机制,值为1时表示自动切换;值为2时,表示基于 MySQL 主从同步的状态决定是否切换;值为3时,表示基于MySQL galary cluster 的切换机制。

配置完成schema.xml文件并且保存后,就可以用过“mycat console”命令启动了。“mycat console”命令可以用于测试配置效果使用,因为这样的启动方式MyCat将会把日志直接输出到屏幕上。

# mycat console
Running Mycat-server...
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
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    | 2016-11-10 00:03:25,369 [INFO ][WrapperSimpleAppMain] total resouces of dataHost dataHost is :2  (io.mycat.backend.datasource.PhysicalDBPool:PhysicalDBPool.java:100)
......
jvm 1    | 2016-11-10 00:03:26,874 [INFO ][WrapperSimpleAppMain] ===============================================  (io.mycat.MycatServer:MycatServer.java:266)
jvm 1    | 2016-11-10 00:03:26,874 [INFO ][WrapperSimpleAppMain] MyCat is ready to startup ...  (io.mycat.MycatServer:MycatServer.java:267)
jvm 1    | 2016-11-10 00:03:26,874 [INFO ][WrapperSimpleAppMain] Startup processors ...,total processors:2,aio thread pool size:4
jvm 1    |  each process allocated socket buffer pool  bytes ,a page size:2097152  a page's chunk number(PageSize/ChunkSize) is:512  buffer page's number is:40  (io.mycat.MycatServer:MycatServer.java:279)
......
jvm 1    | 2016-11-10 00:03:28,396 [INFO ][WrapperSimpleAppMain] using nio network handler   (io.mycat.MycatServer:MycatServer.java:381)
jvm 1    | 2016-11-10 00:03:28,513 [INFO ][WrapperSimpleAppMain] $_MyCatManager is started and listening on 9066  (io.mycat.MycatServer:MycatServer.java:397)
jvm 1    | 2016-11-10 00:03:28,515 [INFO ][WrapperSimpleAppMain] $_MyCatServer is started and listening on 8066  (io.mycat.MycatServer:MycatServer.java:401)
jvm 1    | 2016-11-10 00:03:28,515 [INFO ][WrapperSimpleAppMain] ===============================================  (io.mycat.MycatServer:MycatServer.java:403)
jvm 1    | 2016-11-10 00:03:28,515 [INFO ][WrapperSimpleAppMain] Initialize dataHost ...  (io.mycat.MycatServer:MycatServer.java:407)
......

 

可以看到MyCat启动成功了,接下来就可以使用mysql的客户端进行连接并测试读写操作执行了。

// 连接到mycat,mycat的端口为8066
# mysql -h 192.168.61.144 -P 8066 -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.29-mycat-1.6-RELEASE-20161028204710 MyCat Server (OpenCloundDB)
......

// 接下来执行一个insert语句:
> insert into myuser(Id,user_name,usersex,user_number) values (24,'user24',0,'242424');

// 从读节点查询刚才插入的数据
> select * from myuser;
+----+-----------+---------+-------------+
| Id | user_name | usersex | user_number |
+----+-----------+---------+-------------+
|  1 | 用户1      |       1 |       11111 |
| 22 | 用户22     |       0 |      220220 |
| 23 | 用户23     |       1 |      232323 |
| 24 | user24    |       0 |      242424 |
+----+-----------+---------+-------------+

 

需要注意MyCat并不负责节点主机中设置的写节点和读节点间的数据同步过程,所以您还是需要使用MySQL Replicaion机制完成读写节点的同步工作。另外注意,某些MySQL第三方客户端由于不支持NIO方式,所以连接MyCat还有一些问题,例如MySQL-Front。接下来的文章我们来实际搭建一个MyCat对数据库集群横向拆分和纵向拆分的支持。

posted @ 2017-09-25 18:06  HE_PX  阅读(459)  评论(0编辑  收藏  举报