Mycat与数据访问层

JerryXia 发表于 , 阅读 (0)
将数据库按业务拆分主从复制与读写分离分库分表

不过这些策略往往会对我们现有的业务逻辑造成侵入,使得应用层不仅要考虑业务逻辑的设计实现还要考虑到底层复杂的拆分策略,这实在是一件头疼的事情,好在有一些现成的中间件可以帮我们实现一个统一的数据访问层,从而向应用层透明地提供数据服务。

这些中间件有MySQL Proxy,Ameoba,Cobar,Mycat。其中Ameoba,Cobar,Mycat是一脉相承的,关于他们之间的故事和优劣对比,这里不细说,网上有很多文章。

总之,Mycat是目前功能最强大也是社区最活跃的。官方网址,支持SQL92标准。

他们这两天刚刚发布了1.5.1版本,不过笔者还是用的1.4版。release包下载地址源码地址

下面我们来玩玩mycat。部署之后有几点要做的:

  • 添加MYCAT_HOME到系统环境变量
  • 将主机名加入/etc/hosts文件(否则启动报错)
  • 修改conf路径下的wrapper.conf文件,修改wrapper.java.initmemory和wrapper.java.maxmemory以适应服务器的配置

之后就是mycat的3个核心配置文件:

  • schema.xml用来配置逻辑schema,所谓逻辑schema就是在物理的多个关系数据库服务器集群上,由mycat抽象出来的一个逻辑schema,也就是说我们可以把错综复杂的复制与分库分表规则配置在这里,mycat把这些抽象成简单的单库应用与应用层通信,从而使得数据访问层的分布式设计对应用层是透明的。

  • server.xml用来配置访问逻辑schema时的schema名和用户名密码,以及各种连接参数

  • rule.xml配置数据库分片的规则和算法

我们以一个简单的电商数据库为例:这是它的ER图ER图试想,当数据量非常庞大的时候,我们首先按照业务对数据库进行拆分,基本上可以分成用户,订单,商品3个分库。同时理论上每个分库最好都需要主从复制,因为主从复制不仅可以实现降低读并发而且还可以做到数据备份和高可用性,而订单和商品数量增长很快,尤其是订单表需要分库分表(单单分表不能降低数据库服务器的访问压力)。

由于资源有限,笔者这里用了4台数据库作为示例。

192.168.6.41作为用户数据库和默认数据库
192.168.6.42作为订单数据库
192.168.6.45作为商品数据库,192.168.6.43作为商品数据库的备库

那么schema.xml配置如下

<?xml version="1.0"?>  <!DOCTYPE mycat:schema SYSTEM "schema.dtd">  <mycat:schema xmlns:mycat="http://org.opencloudb/">      <!-- my own schema -->    <schema name="scaffold" checkSQLschema="false" sqlMaxLimit="100" dataNode="userNode">    <table name="cs_address" type="global" dataNode="userNode, orderNode1, orderNode2" />    <table name="cs_cart" primaryKey="id" dataNode="orderNode1, orderNode2" rule="order-sharding-mod-long">      <childTable name="cs_cart_item" joinKey="cart_id"          parentKey="id" primaryKey="id" />    </table>    <table name="cs_order" primaryKey="id" dataNode="orderNode1, orderNode2" rule="order-sharding-mod-long">      <childTable name="cs_order_item" joinKey="order_id"          parentKey="id" primaryKey="id" />    </table>    <table name="cs_category" primaryKey="id" dataNode="goodsNode" rule="shop-sharding-mod-long" />    <table name="cs_goods" primaryKey="id" dataNode="goodsNode" rule="shop-sharding-mod-long" >      <childTable name="cs_goods_gallery" joinKey="goods_id"          parentKey="id" primaryKey="id" />    </table>  </schema>  <dataNode name="userNode" dataHost="userHost" database="scaffold_user" />  <dataNode name="orderNode1" dataHost="orderHost" database="scaffold_order1" />  <dataNode name="orderNode2" dataHost="orderHost" database="scaffold_order2" />  <dataNode name="goodsNode" dataHost="goodsHost" database="scaffold_goods" />  <dataHost name="userHost" maxCon="1000" minCon="10" balance="0"       writeType="0" dbType="mysql" dbDriver="native">    <heartbeat>select user()</heartbeat>    <writeHost host="hostM1" url="192.168.6.41:3306" user="root" password="eros" />  </dataHost>    <dataHost name="orderHost" maxCon="1000" minCon="10" balance="0"       writeType="0" dbType="mysql" dbDriver="native">    <heartbeat>select user()</heartbeat>    <writeHost host="hostM1" url="192.168.6.42:3306" user="root" password="eros" />  </dataHost>  <dataHost name="goodsHost" 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.6.45:3306" user="root" password="eros">      <readHost host="hostS1" url="192.168.6.43:3306" user="root" password="eros" />    </writeHost>  </dataHost></mycat:schema>  

可以看出该配置文件分3部分,schema,datanode,datahost

<schema name="scaffold" checkSQLschema="false" sqlMaxLimit="100" dataNode="userNode">  

表示逻辑数据库名为scaffold,使用userNode指向的物理数据库作为默认数据库,在1.4及之后的版本中,默认物理库无需分片的表可以不在配置文件里配置。

<table name="cs_address" type="global" dataNode="userNode, orderNode1, orderNode2" />  

global表示全局表,数据库中总有些表是其它很多模块都依赖的比如站点配置等等这种表,这里以地址为例,地址是用户模块和订单模块都需要的,所以在dataNode属性里配置所有依赖它的节点。
对于全局表Mycat会自动将表数据复制到所有依赖的节点上,并实时同步。

<table name="cs_order" primaryKey="id" dataNode="orderNode1, orderNode2" rule="order-sharding-mod-long">        <childTable name="cs_order_item" joinKey="order_id"          parentKey="id" primaryKey="id" />    </table>

childTable是利用mycat的ER表的概念,这种一对多关系是数据库设计中经常出现的场景,在分片的情况下其join查找的复杂度会大大增加,因为很可能出现跨库join的情况。因此,mycat的ER表机制,可以保证主表和其对应的从表记录保存在一个分片上,从而防止跨库join。rule属性指定了分片策略,具体在rule.xml中配置。

<dataNode name="orderNode1" dataHost="orderHost" database="scaffold_order1" />  

这里申明dataNode和对应的dataHost名,database属性为物理数据库名。

<dataHost name="goodsHost" 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.6.45:3306" user="root" password="eros">      <readHost host="hostS1" url="192.168.6.43:3306" user="root" password="eros" />    </writeHost>  </dataHost>

为dataHost配置真实的物理链接方式,这里表示商品数据库,采用一主一从的配置。balance=1表示所有读库和备用写库都参加读操作的负载均衡。

在server.xml中配置

<user name="root">         <property name="password">eros</property>       <property name="schemas">scaffold</property>    </user>

设置逻辑schema的名字和访问账户

在rule.xml中配置

<tableRule name="order-sharding-mod-long">          <rule>            <columns>user_id</columns>            <algorithm>mod-long</algorithm>        </rule>    </tableRule>    <tableRule name="shop-sharding-mod-long">        <rule>            <columns>shop_id</columns>            <algorithm>mod-long</algorithm>        </rule>    </tableRule>

申明做分片的列和分片算法,mycat已经实现了大量主流的分片算法,当然我们也可以自定义。

配置完成,建好各个物理库和相应表之后,启动Mycat,因为Mycat实现了mysql协议,所以我们可以像访问mysql物理库一样在控制台访问我们的逻辑库。

mysql -uroot -peros -h127.0.0.1 -P8066

连接mycat

然后就可以看到我们逻辑库了

逻辑库

查看表

查看表

发现我们没有在schema.xml中配置的表并没有显示出来,不用担心,我们可以正常操作这些表的

隐藏表

同样,在项目中,我们只要把jdbc的url改成mycat的域名加8066(默认端口)和逻辑库就可以透明地操作数据库集群了。

更进一步,我们可以把对关系数据库集群的访问和对其它nosql、缓存以及搜索引擎的数据操作通过dubbo或者thrift等工具做成SOA中的一个服务,从而隔离出一个统一的数据访问层,让应用层的开发更专注于业务逻辑。

示例用的数据库建表及初始化语句示例用的mycat配置文件

最后,忠告,数据库分库分表将大大增加系统复杂度,引入跨库join,全局唯一ID和分布式事务处理等一系列棘手的问题,不是万不得已不要分,不要为分布式而分布式。