接上文《架构设计:系统存储(11)——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对数据库集群横向拆分和纵向拆分的支持。

Logo

更多推荐