Welcome to Doris′s Blog

MyCat头脑风暴

高并发高可用架构演进 :

   数据库、应用于一体➡数据库与应用分离➡数据库根据业务将表分到不同的库中➡同一张表进行读写分离➡表中数据根据需求分表

   其中Mycat数据库中间件起到了读写分离,分库,分表的作用

1.解决的问题

  读写分离 (Mysql主从复制)/分库分表 ➡ 多数据源  ➡ Java程序需要进行多个数据源的切换 / Java程序与MySQL紧耦合 —— Java程序与数据库解耦

   高可用 ➡ 集群与容错机制  高并发 ➡ 分库+分表 —— 解决高并发高可用对数据库的压力

2.怎样解决

  Mycat通过配置文件搭建一个逻辑数据库,向外暴露一个Mycat的访问接口[解耦] ,而底层通过多个数据节点DataNode(一个DataNode对应一个DataHost,而一个DataHost保证其高可用搭建读写分离集群writeHost,readHost(一主一从/双主双从)) [高可用],通过<table dataHost="">节点将不同业务的表分发到不同的库中,分散数据库压力,而相同表中数据量大时会遇到瓶颈,所以此时将表中数据根据<table rule="">根据分片规则分发到多个表中(不同库的不同的表),提高查询等的效率[高并发]

  而所有这些操作都在Mycat内部解决,对于Java程序来说完全透明,和平常一样进行SQL的传输即可,Mycat接到SQL语句,对其进行拦截,后根据配置的一系列分库分表规则等进行解析SQL,分析SQL(分片分析,缓存分析,路由分析......),分发到后台的物理数据库,得到结果对结果进行整合后传回

 

 

 3.如何使用

  开发步骤 : 分析业务制定出分片规则等后配置好配置文件 --> 启动对应的物理数据库 -->使用Mycat执行建表操作

  • 读写分离 ➡同一个DataHost的主从复制

       

 

<dataNode name="dn1" dataHost="host1" database="testdb" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="1"
    writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100" >
  <heartbeat>select user()</heartbeat>
  <!-- can have multi write hosts -->
  <writeHost host="hostM1" url="192.168.140.128:3306" user="root" password="123123">
    <!-- can have multi read hosts -->
    <readHost host="hostS1" url="192.168.140.127:3306" user="root" password="123123" />
  </writeHost>   
  <writeHost host="hostM2" url="192.168.140.126:3306" user="root" password="123123">     <!-- can have multi read hosts -->     <readHost host="hostS2" url="192.168.140.125:3306" user="root" password="123123" />   </writeHost> </dataHost>

负载均衡类型,目前的取值有4 种:

(1) balance="0", 不开启读写分离机制,所有读操作都发送到当前可用的 writeHost 上。

(2) balance="1",全部的 readHost 与 stand by writeHost 参与 select 语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且 M1 与 M2 互为主备),正常情况下,M2,S1,S2 都参与 select 语句的负载均衡。

(3) balance="2",所有读操作都随机的在 writeHost、readhost 上分发。

(4) balance="3",所有读请求随机的分发到 readhost 执行,writerHost 不负担读压力

#writeType="0": 所有写操作发送到配置的第一个writeHost,第一个挂了切到还生存的第二个#writeType="1",所有写操作都随机的发送到配置的 writeHost,1.5 以后废弃不推荐#writeHost,重新启动后以切换后的为准,切换记录在配置文件中:dnindex.properties 。
#switchType="1": 1 默认值,自动切换。 # -1 表示不自动切换 # 2 基于 MySQL 主从同步的状态决定是否切换。
  • 分库  ➡ 表分到不同的库中
<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">
  <!--制定规则,customer表分到dataNode2库中-->
  <table name="customer" dataNode="dn2" ></table> </schema>
<dataNode name="dn1" dataHost="host1" database="orders" /> <dataNode name="dn2" dataHost="host2" database="orders" />
<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="192.168.140.128:3306" user="root" password="123123">
  </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="192.168.140.127:3306" user="root" password="123123">   </writeHost> </dataHost>
  • 分表  ➡ 数据分到不同的表中(不同的表又在不同的库中) ➡分表基于分库,但多张表是同一种表
schema.xml
<
schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">   <!--制定规则,customer表分到dn1,dn2两个库中,并且其中的数据根据mode_rule规则分配-->   <table name="customer" dataNode="dn1,dn2" rule="mod_rule"></table> </schema>

 

rule.xml
#在 rule 配置文件里新增分片规则 mod_rule,并指定规则适用字段为 customer_id,
#还有选择分片算法mod-long(对字段求模运算),customer_id 对两个节点求模,根据结果分片
#配置算法 mod-long 参数 count 为 2,两个节点
<tableRule name="mod_rule">
  <rule>
    <columns>customer_id</columns>
    <algorithm>mod-long</algorithm>
  </rule>
</tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod"> <!-- how many data nodes -->   <property name="count">2</property> </function>
    •  E-R表 : 主表进行了分片,其子表也需要根据关联字段进行分片
<table name="orders" dataNode="dn1,dn2"    rule="mod_rule" >
  <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
    • 全局表 : 全局表数据量少,变动不频繁,且很多表都需要其 ➡ 通过数据冗余实现其可以与任意一张表进行关联 ➡ 多个分库中都创建一个全局表,且数据变动一致
<table name="orders" dataNode="dn1,dn2"    rule="mod_rule" >
  <childTable name="orders_detail" primaryKey="id" joinKey="order_id" parentKey="id" />
</table>
<table name="dict_order_type" dataNode="dn1,dn2" type="global" ></table>
    • 分片规则
      • 取模    根据物理数据库数量进行取模轮询分配
      • 分片枚举   适用于根据固定数量固定值进行分配
      • 范围约定(值/日期)

 

    • 全局序列

      表中数据ID自增带来的问题 : 进行了分表,每个表中ID进行自增,会导致出现重复ID,所以,此时自增ID不可取,MyCat对这种情况做了几种解决方案

      1) 本地文件

        MyCat本地保存一个文件,其中记录ID值,每次新增数据时从此文件中取

        优 本地文件读取效率高

        缺 : 当此台Mycat宕机后,启用另一台Mycat时无法获取之前那个本地文件中的ID值,也就无法决定接下来的ID值

      2) 数据库方式 

        在一个数据库中维护一个ID表,每次新增时查此表,但每次新增都查表,会很浪费IO效率极低,所以Mycat会预加载一部分到内存中,这样大部分序列都是在内存年中完成的。这样用完之后再向数据库请求一段,当此台MyCat宕机后,另一台MyCat重新请求一段序列,而之前的序列不管用完或者没用完都不会再管,这样就不会产生冲突

        优将ID值放到了数据库中,与MyCat状态无关 , 避免了MyCat宕机带来的问题

          以一段序列加载进内存的策略来进行ID值分配,提高了效率,减少了IO

      3) 时间戳方式

        时间戳避免了ID冲突问题,但此ID值过长,浪费空间

      4)自定义ID序列

        Java程序中新增时根据业务指定ID 

        利用Redis单线程原子性incr来生成序列

        缺 这些自定义ID都需要在Java代码中指定ID实现,又增加了耦合度,所以不推荐

posted on 2020-04-26 12:15  Doris董  阅读(208)  评论(0编辑  收藏  举报

导航